Dev Container setup
When scaffolding a new Python project, or when adding Dev Containers to an existing one:
Step 1: Ask the user
Ask: "Do you want to set up a Dev Container for this project?"
If no, skip.
If yes, proceed with Step 2.
Step 2: Gather project metadata
Read the project's pyproject.toml:
| Info | Source |
|---|---|
| Python version | requires-python field — extract the minor version (e.g. >=3.12 → 3.12) |
| uv version | [tool.uv] required-version — extract the bare version (e.g. "==0.11.18" → 0.11.18) |
| Sync command | If [dependency-groups] has a dev group, use uv sync --group dev; otherwise use uv sync |
Then extract the uv version from [tool.uv] required-version:
uv_version=$(grep "^required-version" pyproject.toml \
| sed 's/.*"\([0-9.]*\)".*/\1/')
This strips any leading operator (==, >=, ~=) and returns the bare version (e.g. 0.11.18).
Then fetch the image digest for the selected Python version:
curl -s "https://mcr.microsoft.com/v2/devcontainers/python/manifests/2-{python_version}-trixie" \
-H "Accept: application/vnd.oci.image.index.v1+json" \
-H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
-o /dev/null -w "%{header_json}" 2>/dev/null \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('docker-content-digest',['N/A'])[0])"
This returns the manifest list digest (e.g. sha256:132c3dc2b618d194d5d56e4b385e11f5f67b265008a3d7cdddf806ab3ff8572b).
Step 3: Substitute and write
Read assets/devcontainer.json.tmpl and substitute placeholders:
| Placeholder | Value |
|---|---|---|
| {{python_version}} | The minor version from step 2 (e.g. 3.12) |
| {{python_image_digest}} | The SHA256 digest from step 2 (e.g. sha256:132c3dc2b618d194d5d56e4b385e11f5f67b265008a3d7cdddf806ab3ff8572b) |
| {{uv_version}} | The bare uv version from step 2 (e.g. 0.11.18) |
| {{post_create_command}} | The sync command from step 2 (e.g. uv sync --group dev) |
Write the result to .devcontainer/devcontainer.json.
Create the directory before writing:
mkdir -p .devcontainer
The devcontainer-lock.json is auto-generated by VS Code on first build — do not create it manually.
SELinux (Fedora/RHEL): On systems with SELinux enforcing, Docker bind-mounts of files with
user_home_tcontext causePermission deniedinside the container. The--security-opt label=disableflag inrunArgsdisables SELinux separation for the container. It is a no-op on macOS and Windows.
Cache invalidation: Feature options in devcontainer.json are passed as env vars to the install script, but Docker's build cache only tracks files — not env vars. After changing feature options, use Dev Containers: Rebuild Container Without Cache in VS Code.
Step 4: README badge
After creating the Dev Container, add the following badge to README.md:
[](https://containers.dev)