Emily Morehouse

Adding Debug Support to Django with Docker and VS Code

2020-05-01

  1. Install ptvsd as a dev dependency. The latest version at the time of writing is 4.3.2, but check it here.
  2. Add an environment variable to control enabling ptvsd. I opted for using VSCODE_DEBUGGER as part of our docker-compose env-file.
  3. Connect the ptvsd server in your manage.py. I do this just before the standard call to execute_from_command_line(sys.argv), like so:
  """
  Django runs twice to support live-reloading! In order to support live-reloading files and
  attaching to the correct process, we use a bit of Django's internals to check if we need to
  start up our debugger.

  See:
  - https://github.com/django/django/blob/master/django/utils/autoreload.py#L26
  - https://ytec.nl/blog/debugging-django-vscode-without-using-noreload/
  """
  if (os.environ.get("RUN_MAIN") or os.environ.get("WERKZEUG_RUN_MAIN")) and os.environ.get(
      "VSCODE_DEBUGGER", False
  ):
      import ptvsd  # noqa

      ptvsd_port = 4000

      try:
          ptvsd.enable_attach(address=("0.0.0.0", ptvsd_port), redirect_output=True)
          print("Started ptvsd at port %s." % ptvsd_port)
      except OSError:
          print("ptvsd port %s already in use." % ptvsd_port)
  1. Expose the port for the ptvsd server. With docker-compose, you can add the port to the port mappings section, like so:
version: "3"
services:
  ... # Redacted for simplicity!

  # Django application
  app:
    ... # Redacted for simplicity!
    ports:
      - "8000:8000" # django application portundefined you probably already have this
      - "4000:4000" # ptvsd port for debugging
  1. Set up the configuration for VS Code in .vscode/launch.json. You'll probably need to tweak the remoteRoot configuration. Note that I've opted to use port 4000, as it doesn't conflict with my normal port usage.

    {
     "version": "0.2.0",
     "configurations": [
       {
         "name": "[django:docker] runserver",
         "type": "python",
         "request": "attach",
         "pathMappings": [
           {
             "localRoot": "${workspaceFolder}",
             "remoteRoot": "/app"
           }
         ],
         "port": 4000,
         "host": "localhost"
       }
     ]
    }
  2. Document it! Here's a starter snippet for your README:
## Debugging with Docker and VSCode

Support for debugging remotely if you're running with Docker is supported out-of-the-box.

To debug with Docker:

1. Set the `VSCODE_DEBUGGER` value to `True` in your docker.local.env

2. Rebuild and run your Docker containers as usual: `docker-compose up --build`

3. Start the debug session from VS Code for the `[django:docker] runserver` configuration

   1. Open up the debugger by by pressing `⌘-⇧-d`, or using the Command Palette and selecting
      `View: Show Run and Debug`.

   2. Select `[django:docker] runserver` from the dropdown near the Play button in the top left.

   3. Hit the Play button or hit `F5` to start debugging

      - Logs will redirect to your integrated terminal as well.

4. Set some breakpoints!

   - You can create a breakpoint by clicking to the left of a line number. When that code is
     executed, the debugger will pause code execution so you can inspect the call stack and
     variables. You can either resume code execution or manage code execution manually by stepping
     into the next pieces of code, or over them.