Loading services with launch.json
Debugt has no service list of its own. A service is a launch configuration — the same entry F5 uses — so anything VS Code can debug, Debugt can run.
The idea
Debugt starts each service through VS Code’s own debugger. It does not read a Dockerfile, a
package.json script or a process file: it reads .vscode/launch.json, and every named
configuration in it is one service.
That means there is nothing new to learn or maintain. If pressing F5 on a configuration starts your service with breakpoints working, Debugt can run it, trace it and put it on a canvas — and your breakpoints still work, because it is still VS Code’s debugger running it.
- A project is a folder with a
.vscode/launch.json: a folder you have open in VS Code, or one inside it that you picked (see Folders inside a folder). - A service is one entry of that file’s
configurations. Itsnameis the service’s name everywhere in Debugt.
A minimal example
A repository with one Node API:
{
"version": "0.2.0",
"configurations": [
{
"name": "Shop API",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/src/server.js",
"env": { "NODE_ENV": "development" }
}
]
}
Open the folder in VS Code and run Debugt: Open UI from the command palette. The folder is listed under Projects in the left pane; select it and Shop API is under Services, ready to drag onto a canvas.
A repository that holds several services lists them all in the one file:
{
"version": "0.2.0",
"configurations": [
{
"name": "Shop API",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/services/shop-api",
"program": "${workspaceFolder}/services/shop-api/src/server.js"
},
{
"name": "Inventory",
"type": "debugpy",
"request": "launch",
"cwd": "${workspaceFolder}/services/inventory",
"module": "uvicorn",
"args": ["app.main:app", "--port", "3001"]
}
]
}
What Debugt reads from a configuration
| Field | What Debugt does with it |
|---|---|
name |
The service’s name. It is how a service is told apart, so keep names unique within a file. |
type |
Which debugger starts it, and so which tracer goes in with it — see the table below. |
request |
Use "launch". A configuration that attaches to a process started elsewhere can be listed and run, but that process was started without Debugt’s tracer and reports no hops. |
cwd |
Where the service runs. Set it to the service’s own folder: it is where Debugt looks for the service’s files, its serverless.yml, and the edits that mark a running service as edited — and only code under it is recorded as the service’s own. |
program |
The file that starts it. Checked before the service is started, so a wrong path is reported in Debugt in plain words rather than as a stack trace in the Debug Console. |
env |
Passed through unchanged. Debugt adds a few variables of its own beside yours (below), and where you set the same one, yours wins. |
serverReadyAction.pattern |
Used as the service’s ready pattern: the service turns green when a line of its output matches. |
console |
Leave it unset or "internalConsole" if you rely on a ready pattern. Output sent to "integratedTerminal" or "externalTerminal" cannot be read, so only the port can be watched. |
Everything else — runtimeExecutable, runtimeArgs, args, envFile, python, preLaunchTask
— is the debugger’s, and is handed to it as written.
cwdandprogramare not strictly required by VS Code, but set both. Withoutcwd, a service runs in the project’s top folder, and Debugt has to guess which files are its own.
Which debugger types are traced
type |
Language | Hops | Execution |
|---|---|---|---|
node |
Node.js — JavaScript and TypeScript | Yes | Yes |
debugpy (or the older python) |
Python | Yes | Yes, on Python 3.12 and later |
java |
Java, Kotlin, Scala | Yes | Yes |
go with "mode": "debug", "auto" or "test" |
Go | Yes | Yes |
php |
PHP | Yes | Not yet |
A configuration of any other type is still a service: it is listed, runs, stops and shows on a canvas. It simply reports no hops of its own. What each language covers in detail is on Language support and limits.
Examples for each language
Node.js
{
"name": "Shop API",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/src/server.js"
}
A service started through a package script works the same way, since the tracer is loaded
through NODE_OPTIONS and reaches whatever Node process the script starts:
{
"name": "Shop API",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"]
}
Python
{
"name": "Inventory",
"type": "debugpy",
"request": "launch",
"cwd": "${workspaceFolder}",
"module": "uvicorn",
"args": ["app.main:app", "--port", "3001"]
}
A script works as well: "program": "${workspaceFolder}/app.py" in place of module and
args. Nothing is installed into your virtual environment.
Java, Kotlin, Scala
{
"name": "Dispatch",
"type": "java",
"request": "launch",
"cwd": "${workspaceFolder}",
"mainClass": "com.example.dispatch.Application",
"projectName": "dispatch"
}
Go
{
"name": "Catalogue",
"type": "go",
"request": "launch",
"mode": "debug",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/cmd/server"
}
The debugger has to build the service for it to be traced, so "mode": "exec" (a binary
built beforehand) and attaching are not. The first start after installing Debugt takes a few
seconds longer while Go’s net/http is compiled once with the tracer in it.
PHP
Start PHP’s built-in server directly, so that the PHP serving requests is the one Debugt started:
{
"name": "Ratings",
"type": "php",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-S", "localhost:8000", "-t", "public", "public/index.php"],
"port": 9003
}
php artisan serve and symfony server:start start a second PHP process of their own, which
is not traced; and PHP run by Apache, nginx or a container is started by something else
entirely. VS Code’s Listen for Xdebug configuration only waits for such a process, so it has
nothing to trace.
Telling Debugt where a service listens
A launch configuration says how to start a service, but not which address it answers on.
Debugt asks for that once, as the service’s Host — http://localhost:3000 — when you first
put an API service on a canvas. It is saved with the project, not in your repository, and can
be changed later in the service’s details.
An API service needs a host before it can be run from a canvas. For one that listens on nothing — a worker that only makes calls — tick It has no port instead.
The host does three jobs:
- It is where the request runner sends that service’s requests.
- It is what a redirect drawn into the service points other services’ calls at.
- Its port is watched to know when the service is up, and is handed to the service as the
PORTenvironment variable — which many dev servers read — so the two ends agree. APORTyour configuration sets itself wins.
When a service is “running”
VS Code calls a debug session started the moment the debugger attaches, which is before your service has connected to its database. Debugt shows a service as starting until one of these says it is really up, in this order:
- Its Ready pattern matches a line of its output. Set one in the service’s details — a
regular expression such as
listening (at|on)— or leave it empty to use the configuration’sserverReadyAction.pattern. - Its port accepts a connection, for a service whose host names a port.
- Neither applies: it is shown as running as soon as it starts.
What Debugt adds when it starts a service
Debugt never edits launch.json. When you press Run it starts a copy of the configuration
with a few additions, which go away when the service stops:
| Language | How the tracer gets in |
|---|---|
| Node.js | --require entries appended to NODE_OPTIONS. Yours are kept. |
| Python | A folder put first on PYTHONPATH. Yours is kept after it. |
| Java | A -javaagent added to vmArgs. |
| Go | -overlay added to buildFlags. |
| PHP | -d auto_prepend_file=… added to the front of runtimeArgs. |
Beside that, a handful of environment variables that all start with DEBUGT_ — who the service
is and where to report its hops. Nothing is installed into your project, and nothing is written
into your repository.
Two things to know:
- One slot each. Go allows one
-overlayand PHP oneauto_prepend_file. A configuration that already sets its own keeps it, and is then not traced. - A service started with F5 is shown as running on the canvas and can be stopped from there, but it was started without the tracer. Stop it and start it from Debugt to see its hops.
Folders inside a folder
VS Code reads launch.json only from the top of each folder you have open. If you open a
folder that gathers repositories — say ~/Projects, with thirty checkouts in it — VS Code
sees none of their configurations.
Debugt does that part itself. Click the folder icon in the Projects header to open Add
projects from folders: it lists the folders below the ones you have open that have a
.vscode/launch.json of their own, with the services each would bring. Tick the ones to add. For those,
${workspaceFolder} means the picked folder, as it would if you had opened it by itself.
Three things do not carry over for a picked folder, and Debugt says so beside it:
- Tasks. A configuration with a
preLaunchTaskorpostDebugTaskhas the task skipped: VS Code looks for tasks in the folder it has open, not the picked one. - Compounds. These are not services and are not listed. Run the services together from a canvas instead.
${input:…}and${config:…}, which VS Code fills in from the folder it has open.
Alternatively, add each repository to a VS Code multi-root workspace: every folder in it is a project with nothing lost.
Several repositories, several windows
Services from different repositories can share one canvas. Either open the repositories as folders of one workspace, or open each in its own VS Code window: the windows find each other, and Debugt’s page lists every window’s projects. Each service is started in the window that has its project open, so its breakpoints are hit there.
Compound configurations
A compounds entry starts several configurations at once. Debugt does not list compounds as
services, because a canvas does the same job and more: it starts the services you drew, in
order, tells you which failed and why, and can redirect calls between them.
When a configuration is renamed or removed
A node on a canvas is tied to its configuration’s name. If that name disappears from
launch.json, the node says not in launch.json — or renamed to …? when another
configuration looks like the same service under a new name. Click it, or choose Point this
node at… from the node’s menu, and pick what it is called now: its nodes on every canvas, its
host, its kind and its requests go with it.
When a service will not start
Debugt checks what it can before handing a configuration to the debugger, and reports what it finds in its own words:
| Message says | Fix |
|---|---|
| The folder it runs in does not exist | cwd points somewhere that is not there. |
| The file it runs does not exist | program is wrong, or is relative to a cwd you did not set. |
| It names nothing to run | Add program (or module for Python), and cwd. |
| The Free plan runs 5 services at once | Stop one, or see Settings → License. |
Anything past that point — a missing dependency, a port in use, a syntax error — is the service’s own failure, and shows in VS Code’s Debug Console as it would with F5.
Checklist
- One configuration per service, each with a clear
name. "request": "launch".cwdset to the service’s own folder, andprogramto the file that starts it.- Output going to the Debug Console if you want a ready pattern.
- F5 starts it and breakpoints are hit. If that works, Debugt works.