Debugging a Poetry virtualenv inside a DevContainer
Poetry creates its virtualenv in a cache directory by default, so when VS Code opens a DevContainer it often selects the system Python instead of the project environment — breakpoints never bind, imports show as unresolved, and python -m pytest works in the terminal but not the debugger. This page makes the virtualenv path deterministic and points the editor at it so debugging works on the first attach. It builds on the Python DevContainer Setup with Poetry & venv cluster.
Prerequisites
- Poetry 1.8+ and Python 3.11+ in the container
- The VS Code Python extension installed via
customizations.vscode.extensions - A
pyproject.tomlwith your dependencies
How-To Steps
-
Force Poetry to create the virtualenv in the project directory so its path is predictable and survives editor restarts.
{ "image": "mcr.microsoft.com/devcontainers/python:3.11@sha256:5d1a...", "containerEnv": { "POETRY_VIRTUALENVS_IN_PROJECT": "true" }, "postCreateCommand": "poetry install --no-interaction", "remoteUser": "vscode" }Verify:
poetry env info --pathshould print/workspace/.venv. -
Point the Python extension at the in-project interpreter so it stops guessing:
{ "customizations": { "vscode": { "extensions": ["ms-python.python", "ms-python.debugpy"], "settings": { "python.defaultInterpreterPath": "${containerWorkspaceFolder}/.venv/bin/python", "python.terminal.activateEnvironment": true } } } } -
Add a debug configuration that uses the same interpreter. Create
.vscode/launch.json:{ "version": "0.2.0", "configurations": [ { "name": "Debug pytest", "type": "debugpy", "request": "launch", "module": "pytest", "args": ["-x", "tests"], "console": "integratedTerminal", "justMyCode": false } ] }Verify: set a breakpoint and run the config — it should bind, not show a hollow red dot.
-
Confirm the interpreter the extension resolved matches Poetry’s:
poetry run which python # Compare with the Python extension's selected interpreter in the status bar
Common Pitfalls
| Symptom | Root Cause | Remediation |
|---|---|---|
| Breakpoints stay hollow | Debugger using system Python, not the venv | Set python.defaultInterpreterPath to .venv/bin/python |
.venv missing after rebuild | Virtualenv stored in the global cache, wiped on rebuild | Set POETRY_VIRTUALENVS_IN_PROJECT=true |
| Imports unresolved in editor only | Extension indexed before poetry install finished | Re-run “Python: Select Interpreter” after postCreateCommand |
justMyCode hides library frames | Default debugpy setting | Set "justMyCode": false in the launch config |
Conclusion
The invariant: the virtualenv must live at a path the editor can name statically, and the Python extension must be told that exact path. Pin POETRY_VIRTUALENVS_IN_PROJECT=true and python.defaultInterpreterPath, and the debugger binds deterministically on every attach.
FAQ
Why does the terminal find the right Python but the debugger doesn’t?
The terminal activates the venv via Poetry’s shell hooks; the debugger uses python.defaultInterpreterPath independently. Set that path explicitly so both agree.
Should I commit .venv?
No. Keep it in the project for path predictability but gitignore it — it is rebuilt by poetry install in postCreateCommand.
Related
- Python DevContainer Setup with Poetry & venv — the parent cluster for Python environments.
- Optimizing Python DevContainer for data science — caching and multi-stage builds for heavier stacks.