VS Code
Container debugging through VS Code, using Node.js as the concrete example. A Dockerfile contains build instructions, an image is the packaged filesystem and startup configuration produced by those instructions, and a container is one running instance of that image.
Debug a Node.js container with VS Code
Regular Container Tools flow
The regular flow requires the VS Code Container Tools extension, listed as Docker in Extensions. The extracted application directory is opened as the workspace. A workspace is the host directory VS Code treats as the root of the project:
code <APPLICATION_DIRECTORY>The image is built and started from the existing Dockerfile:
Dockerfile -> right click -> Build Image...
-> enter an image tag or keep the default
Containers panel -> Images -> <IMAGE> -> <TAG> -> Run
Run Interactive performs the same start while keeping the container logs visible. Once the application is running, the run icon under Run and Debug uses the selected project launch configuration to attach the debugger. A launch configuration is a .vscode/launch.json entry that tells VS Code which debugger to use, whether to launch or attach, and how local source paths correspond to paths in the running process.
Only one container can bind a particular host port at a time. Bind for 0.0.0.0:<PORT> failed: port is already allocated means that an earlier container or another local process already owns that port. The earlier container can be stopped from Containers panel -> Containers -> <CONTAINER> -> right click -> Stop before starting the debug container.
Container files remain accessible from:
Containers panel -> Containers -> <CONTAINER> -> Files
An interactive shell is available from:
Containers panel -> Containers -> <CONTAINER> -> right click -> Attach ShellDockerfile debugger error
VS Code may report that no extension is available for debugging the active Dockerfile and offer to search the Marketplace. The project launch configuration supplies the application debug target.

It appears when the active editor contains a Dockerfile and no applicable project launch configuration is selected. Run and Debug then treats that file as the requested debug target. A Dockerfile contains the instructions for building an image. The application process running inside the resulting container is the required attachment target.
The project launch configuration under Run and Debug is the regular attachment method. When no launch configuration is available, Dev Containers provides access to the already-running container:
Dev Containers extension
-> Ctrl+Shift+P
-> Dev Containers: Attach to Running Container...
-> <CONTAINER>
-> File -> Open Folder -> <REMOTE_ROOT>Dev Containers starts a separate VS Code window whose file operations and terminals run inside the selected container. The bottom-left Container: ... indicator confirms that the new window uses this remote environment. Paths opened in that window are container paths rather than paths on the host.
A Dockerfile COPY instruction stores the source files as an image layer at build time. The running container therefore contains a snapshot rather than a live reference to the host directory. Later host changes require another Build Image... unless a bind mount explicitly maps the host directory over the container path.
Find by: vscode, container tools, docker debug, dockerfile debugger error, run and debug, dev containers, attach running container, remote root, copy snapshot
Attach to a running Node.js process inside the container
Opening a Dev Containers window provides access to the container filesystem and process list. It does not attach the JavaScript debugger automatically. Attaching connects VS Code’s debugger to an already-running Node.js process instead of starting another copy of the application.
Dev Containers window -> <REMOTE_ROOT>
-> Ctrl+Shift+P
-> Debug: Attach to Node Process
-> node <ENTRYPOINT>
-> breakpoints under <REMOTE_ROOT>When the image starts through CMD ["npm", "start"], the process list commonly contains both an npm parent and the actual node <ENTRYPOINT> application process. The Node.js process is the debug target.
npm start
└── node <ENTRYPOINT> <- attach hereFind by: vscode, dev containers, attach node process, running container, npm parent, node entrypoint, remote debugging, breakpoint
Prepare the image for VS Code debugging
The complete rebuild-based workflow changes package.json and the Dockerfile, then adds .vscode/launch.json for VS Code. Node Inspector is the debugging interface built into Node.js; it permits a debugger to pause execution, inspect variables, and control stepping.
Add the debug command to package.json
Current configuration:
{
"scripts": {
"start": "node <ENTRYPOINT>"
}
}Updated configuration with a separate Node Inspector command:
{
"scripts": {
"start": "node <ENTRYPOINT>",
"debug": "node --inspect=0.0.0.0:9229 <ENTRYPOINT>"
}
}Start the debug command from the Dockerfile
Current configuration containing the CMD line to replace:
EXPOSE <CONTAINER_APP_PORT>
CMD ["npm", "start"]Updated configuration with EXPOSE 9229 added and the existing CMD ["npm", "start"] line replaced by CMD ["npm", "run", "debug"]:
EXPOSE <CONTAINER_APP_PORT>
EXPOSE 9229
CMD ["npm", "run", "debug"]Only the new CMD line remains in the Dockerfile. EXPOSE 9229 records the intended inspector port as image metadata; it does not publish that port on the host. npm run debug starts the application with --inspect=0.0.0.0:9229, which makes Node Inspector listen on every container network interface whenever the rebuilt image runs.
Add the VS Code attachment configuration
The documented Dockerfile uses /app as its working directory and keeps src/ as a subdirectory:
WORKDIR /app
COPY ./src ./srcFor this source layout, .vscode/launch.json is added exactly as shown. No values require replacement:
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to containerized Node.js",
"type": "node",
"request": "attach",
"address": "127.0.0.1",
"port": 9229,
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app",
"skipFiles": ["<node_internals>/**"]
}
]
}localRoot identifies the source tree opened on the host. ${workspaceFolder} is replaced by the current VS Code workspace directory. remoteRoot identifies the same source tree inside the container. VS Code joins the remainder of each file path to these two roots, producing this mapping:
${workspaceFolder}/src/index.js -> /app/src/index.jsThe modified image is rebuilt and started through the regular Container Tools flow. Docker: Attach to Node under Run and Debug then connects VS Code to the inspector at 127.0.0.1:9229.
A hollow breakpoint marked as unbound means that the debugger has not matched the local file and line to a script loaded by the Node.js process. For the documented layout, ${workspaceFolder}/src/index.js must refer to the same file as /app/src/index.js. A different WORKDIR, COPY destination, or workspace root requires corresponding localRoot and remoteRoot values.
Find by: rebuild debug image, package json debug script, dockerfile inspect, node inspector, inspect 9229, launch json, expose port
Place breakpoints and inspect variable scope
A breakpoint on a function declaration may pause only while the module initially defines the function. A breakpoint intended to pause on every call belongs on an executable statement inside the function body.
const transformedValue = transform(inputValue);
const nextValue = consume(transformedValue);A breakpoint on the first line exposes inputValue before transform() runs. transformedValue does not exist yet. Stepping over the line executes transform() and makes its return value available. A breakpoint on the second line exposes both values immediately.
F10 Step Over execute the current line without entering the called function
F11 Step Into enter the function called by the current line
Shift+F11 Step Out finish the current function and return to its caller
F5 Continue resume execution until the next breakpointA scope is the region of code in which a variable name can be resolved. Function parameters and variables declared inside the current function appear under Local. A closure is the retained outer scope used by an inner function; module-level or outer-function variables referenced by the current function appear under Closure. Runtime-provided names available throughout the process appear under Global.
The call stack records the active chain of function calls. Each listed stack frame represents one paused function call and has its own local and closure scopes. Selecting another frame changes which variables Hover, Watch, and the Debug Console resolve.
Find by: breakpoint placement, step over, step into, step out, continue, f10, f11, shift f11, local scope, closure, watch, debug console
Watch variables across scopes
A Watch expression is a variable name or JavaScript expression that VS Code evaluates every time execution pauses. It can be added through the + icon on the right side of the WATCH heading. VS Code resolves it from the selected stack frame, regardless of whether the referenced name appears under Local, Closure, or Global.
WATCH -> + -> <VARIABLE_OR_EXPRESSION>
Watch expressions remain visible while stepping through the application. The selected stack frame controls the scope used to resolve each expression.
Find by: vscode watch, watch variable, watch expression, search variables, local, closure, global, paused stack frame, inspect value
Send requests to the debug instance
A debugger attached to a local container observes only requests handled by that container.
request -> http://127.0.0.1:<HOST_APP_PORT> -> local container -> breakpoint
request -> remote deployment -> no local breakpointThe browser or interception proxy destination must point to the published local application port. Changing only the HTTP Host header does not necessarily change the network destination selected by the proxy.
Authentication state belongs to the application instance that created it. A session cookie from a remote deployment may fail against the local process when session storage, signing secrets, or application state differ.
Find by: local container, remote target, breakpoint not hit, proxy destination, host header, published port, session cookie, debug instance