This is absolutely just a post written for me so I have a reference point to come back to when I forget how to do this in like 2 weeks!
I'm currently a backend engineer, working in Python, using Visual Studio Code as my editor of choice. And at the company I work at, my team has already done the hard work of Docker-izing all of it's applications and leveraging docker-compose
files for local development. Additionally, our team is very live-and-let-live when it comes to each team member picking their own personal IDEs and setup, so I use VS Code, others use VS Code + devcontainers, others use Pycharm, others use emacs, etc etc etc.
Fortunately, VS Code provides great debugging tooling for Python and for Docker! Unfortunately, all of my googling so far led to docs about how to create dockerfiles or docker-compose files for use in debugging, and typically for standalone applications. I couldn't find anything to clearly lay out how to start debugging on an already-existing docker-compose file that may contain many different services and containers other than the app you're trying to debug (like dependencies or databases) and that has to be maintained for all the other people working on the application alongside you.
So, that's what this is! It works! Maybe there's a better way, but this is My Way!
1: Install debugpy
debugpy
is the Python debugging tool that VS Code uses, so you need to make sure it's installed on the container that will run the application. If you have a requirements file (requirements.in
or requirements.txt
) include debugpy
in that. If you separate out requirements files for local, dev, or test environments separate from your production requirements, I'd highly recommend only including it in your local/dev/test!
Alternatively, make sure that when your Docker container is created you at some point run pip install debugpy
prior to starting up your actual application.
2: Expose port 5678
In your docker-compose file, for the service you want to debug, make sure to expose port 5678
. This involves adding a line something like this:
services:
my-cool-service-name:
...
ports:
# any existing port mappings you have
- "5678:5678"
...
This tells Docker to expose the container's local 5678
port as port 5678
on the Docker network (which best I can tell will default to localhost
)
3: Start the app in the context of debugpy
In your docker-compose file there's probably some command
that is given to container on startup and that probably starts the application. At the very least, all the examples I have to deal with do!
If so, one of those commands will probably either be a python command to start the application (ex for django something like python manage.py runserver
) or, if you're running a web application, some command to start up a webserver (ex for a FastAPI app using uvicorn, uvicorn app.main:app
)
If it's the former, then modify that command to start up debugpy as part of it. If for example it's a django app with the command python manage.py runserver
, change that to python -m debugpy --wait-for-client --listen 0.0.0.0:5678 manage.py runserver
This tells python to start up debugpy
, have it halt execution of the subsequent script until a debugger is connected, and to listen on port 5678
of host 0.0.0.0
If you aren't already starting your app via a python
call, then you can turn it into one with python -m
. If for example you have a FastAPI app with a startup command like uvicorn app.main:app
then you can modify it to python -m debugpy --wait-for-client --listen 0.0.0.0:5678 -m uvicorn app.main:app
This does the same thing as the django example, except instead of debugpy overseeing the running of a manage.py
script, it oversees python running the uvicorn
command.
4: Add a launch.json configuration
In .vscode/launch.json
add the following to the configurations
array:
{
"configurations": [
{
"name": "Attach (remote debug)",
"type": "python",
"request": "attach",
"port": 5678,
"host": "127.0.0.1",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
}
]
}
This creates a command for VS Code called "Attach (remote debug)" that will tell it to attach a python debugger to 127.0.0.1:5678
. If you've been paying attention, that should mean the debugger is now attaching to the port exposed by the application container that is being listened to by debugpy.
5: Start debugging!
Run docker-compose up
to start up your application like normal. It should hang on startup because we passed that --wait-for-client
flag in the initial debugpy command.
Then in VS Code go to "Run and Debug" (cmd-shift-D), select "Attach (remote debug)", and press the green triangle.
From there, your debugger should attach to the running container, the rest of the application startup should continue, and you should be able to do all the cool fun stuff the VS Code debugger allows!