First time I tried attaching the VS Code debugger to a container running as part of a Docker Compose stack, it just… didn’t connect. No error, no breakpoint hit, nothing — the app ran fine, the debugger just sat there like it wasn’t even trying. Setting up and debugging a multi-container app in VS Code with Docker Compose comes down to getting three things right together: your compose file exposing a debug port, your Dockerfile actually running the app in debug mode, and a launch.json configuration that knows how to attach to the right container. Miss any one of those and you get exactly what I got — a running app and a debugger that’s just watching from outside.
So let’s set this up properly and cover what actually breaks when it doesn’t work.
Quick Answer
- Install the Dev Containers and Docker extensions in VS Code
- Add a debug-specific service (or override file) that exposes your language’s debug port and runs the app in debug mode, not production mode
- Write a launch.json with an “attach” configuration pointing to that exposed port
- Start your stack with
docker compose up, then run the attach configuration from VS Code’s Run and Debug panel - If breakpoints aren’t hitting, check that your source code paths inside the container match what your launch.json expects
That’s the shape of it. The details differ a bit by language, which I’ll get into below.
Why Multi-Container Debugging Breaks More Than Single-Container Setups
Debugging a single containerized app is fairly forgiving. Multi-container setups add failure points that don’t exist when you’re only dealing with one service.
Debug ports not exposed correctly. Your app needs to actually listen for a debugger connection on a specific port (5678 for Python’s debugpy, 9229 for Node, and so on depending on language), and that port needs to be both exposed in your Dockerfile and mapped in your docker-compose.yml. Miss either half and the debugger has nowhere to connect, even if the app itself runs fine.
Production vs debug mode mismatch. A lot of Dockerfiles are written to run the app in a straightforward “just start it” way that’s fine for production but doesn’t include the debug adapter or debug flags needed for VS Code to actually attach. You typically need a separate debug-oriented startup command, not the same one you’d use in production.
Path mapping issues between host and container. VS Code’s debugger needs to map the file paths inside the container back to the actual files on your machine so breakpoints line up correctly. If your sourceFileMap or equivalent path mapping is wrong, you can end up with a debugger that’s technically attached but never actually stops at your breakpoints.
Network isolation between services. Docker Compose puts your services on their own internal network by default, which is normally exactly what you want — but it also means the debugger connection from your host machine to a specific container has to go through the ports you’ve explicitly published, not just whatever’s happening on the internal compose network.
Multiple services needing debugging at once. If you’ve got a frontend and backend both running as separate containers and you need to debug both simultaneously, you need separate debug configurations and separate exposed ports for each, and it’s easy to have one working while the other silently isn’t.
Setting Up the Project Structure
Step 1: Install the Right Extensions
You’ll want the Dev Containers extension and the Docker extension from the VS Code marketplace. Dev Containers handles a lot of this more smoothly if you go that route instead of a pure docker-compose attach setup, but both approaches work — I’ll cover the docker-compose attach method here since it’s more flexible for existing multi-service projects that weren’t built around Dev Containers from the start.
Step 2: Create a Debug-Specific Compose Override
Rather than modifying your main docker-compose.yml directly, create a docker-compose.debug.yml file that overrides just what’s needed for debugging:
yaml
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile.debug
ports:
- "5678:5678"
command: python -m debugpy --listen 0.0.0.0:5678 --wait-for-client -m flask run --host=0.0.0.0And this keeps your production compose file clean while giving you a debug variant you run separately with docker compose -f docker-compose.yml -f docker-compose.debug.yml up.
Step 3: Adjust Your Dockerfile for Debug Mode
Your debug Dockerfile needs the debug adapter installed. For Python, that’s debugpy; for Node, the inspector is built in, so it’s more about flags than an extra install. A minimal example for Python:
dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt debugpy
COPY . .
EXPOSE 5678Step 4: Write the launch.json Attach Configuration
In .vscode/launch.json, add an attach configuration matching your exposed port:
json
{
"version": "0.2.0",
"configurations": [
{
"name": "Docker: Attach to Backend",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}/backend",
"remoteRoot": "/app"
}
],
"justMyCode": false
}
]
}The pathMappings section is the part people skip and then wonder why breakpoints don’t work — localRoot needs to match where your files actually live on your machine, and remoteRoot needs to match the WORKDIR in your Dockerfile exactly.

Debugging Multiple Services at Once
If you need to debug a frontend and backend simultaneously, you’ll want a compound launch configuration. Add both individual attach configs, then a compound section that runs them together:
json
{
"compounds": [
{
"name": "Debug Full Stack",
"configurations": ["Docker: Attach to Backend", "Docker: Attach to Frontend"]
}
]
}Each service needs its own exposed debug port — you can’t share one port across two containers, obviously, so double-check your compose override has distinct port mappings for each service you’re debugging.
Common Debug Setup Issues Compared
| Issue | Symptom | Fix Difficulty | Notes |
|---|---|---|---|
| Debug port not exposed | Debugger can’t connect at all | Easy | Check both Dockerfile EXPOSE and compose ports |
| App running in production mode | Connects but doesn’t behave like debug session | Medium | Check your command/entrypoint override |
| Wrong path mapping | Attaches but breakpoints don’t hit | Medium | Match localRoot/remoteRoot exactly |
| Compose network conflicts | Intermittent connection issues | Medium-Hard | Usually port collision with another service |
| Missing debug adapter in image | Connection refused immediately | Easy | Confirm debugpy/inspector actually installed |
What Actually Worked For Me
The first time I set this up, I genuinely thought I had everything right — port exposed, launch.json written, extension installed. But the debugger just wouldn’t attach, no error message, just a timeout after a while. Spent a good chunk of time assuming it was a VS Code extension issue since that felt like the more likely villain at the time.
Turned out my Dockerfile was still running the app with the regular startup command, not the debugpy-wrapped one, because I’d built the debug override file but forgotten to actually reference it in my docker compose -f command and was just running the regular compose file out of habit. The app was running completely normally, from its own perspective it had nothing to attach to since debugpy was never actually started inside the container in the first place.
That’s not entirely accurate to call it a “VS Code problem” like I originally assumed — it was a me problem, running the wrong compose command. Once I actually used the -f docker-compose.yml -f docker-compose.debug.yml combo consistently, it connected on the first try.
Advanced Fixes and Edge Cases
Hot reload conflicts with debug attach. Some frameworks with built-in hot reload (like Flask’s debug mode or certain Node dev servers) can conflict with an external debugger attaching, since both are trying to manage the process lifecycle. You may need to disable framework-level hot reload while using VS Code’s debugger, and rely on VS Code’s own file-watching instead if you need that workflow.
Container restart wiping your debug session. If your compose setup includes a restart policy, and the app crashes on a bad breakpoint interaction, Docker might restart the container and drop your debug session entirely without much explanation. Setting restart: "no" in your debug override while actively debugging avoids this confusion.
Checking container logs when attach silently fails. Run docker compose logs -f backend (or whatever your service is named) while attempting to attach. If debugpy or the inspector never logged a “waiting for debugger” message, the debug adapter isn’t actually starting, which points you back to the Dockerfile/command setup rather than a VS Code-side issue.
Debugging across a reverse proxy setup. If your multi-container app includes something like Nginx routing traffic between services, make sure your debug port mapping bypasses the proxy entirely — attach the debugger directly to the backend container’s exposed port, not through whatever URL the proxy serves the app on.
Prevention Tips
- Keep debug compose overrides separate from your production compose file rather than toggling settings back and forth in one file
- Double-check pathMappings any time you restructure your project directories
- Use
docker compose logs -fas your first troubleshooting step before assuming it’s a VS Code configuration problem - Document your exact debug startup command somewhere in the repo (a README section works fine) since it’s easy to forget the right
-fflag combination months later
FAQ
Do I need the Dev Containers extension for this, or just Docker? Just Docker plus the language debugger extension (Python, Node, etc.) is enough for the attach-to-running-container approach covered here. Dev Containers is a different, more integrated workflow that’s worth looking into separately if you’re starting a new project from scratch.
Why does my debugger connect but breakpoints never trigger? That’s almost always a path mapping issue — the debugger’s attached and working, it just can’t match the file paths inside the container to the files you’ve set breakpoints on locally.
Can I debug more than two containers at once? Yeah, just extend the compound configuration with more entries, and make sure each service has its own distinct exposed debug port.
Is it normal for debug mode to be noticeably slower than production mode? Yes, that’s expected. Debug adapters add overhead, and if you set --wait-for-client, the app won’t even start processing requests until VS Code actually attaches.
Does this setup work the same way for Node and Python? The overall pattern’s the same, but the specific debug adapter and port differ — debugpy on 5678 for Python, the built-in inspector on 9229 for Node, so double-check your language’s specific setup docs for exact flags.
Editor’s Opinion
took me way longer than it shouldve to realize i was just running the wrong compose command the whole time, not some deep vs code bug. keeping a separate debug compose file is honestly the move, dont try to cram debug flags into your main file with env var toggles, past me tried that and it got messy fast