A devcontainer.json tells your IDE — GitHub Codespaces or local VS Code — how to build the environment your code runs in: which image, which tools, which ports, which extensions. One file, and every contributor gets the same setup.
image vs build
The base can be a prebuilt image (fastest, most common):
{ "image": "mcr.microsoft.com/devcontainers/javascript-node:20-bookworm" }
or a build pointing at a Dockerfile when you need custom bake-in steps. Set one, not both.
features: reusable toolchains
features are versioned add-ons you compose in without writing Dockerfile lines — a language runtime, the GitHub CLI, or Docker itself:
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": { "moby": true }
}
Want a different stack? Swap the base image and one feature — that is the difference between the "Python", "Go" and "Node" configs people search for.
forwardPorts
List the container ports you want on your machine. portsAttributes labels them and controls auto-forward behavior:
"forwardPorts": [3000, 5432]
customizations.vscode
Pre-install extensions and apply settings the moment the container launches:
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
"settings": { "editor.formatOnSave": true }
}
}
postCreateCommand
Runs once after the container is created — the right place for npm install or pip install -r requirements.txt. Keeping install steps here (not in the image) keeps the image cacheable.
Security notes
- Never bake secrets into the image or this file — use Codespaces secrets or an env file.
docker-in-dockeris privileged; add it only when the workflow needs it.- Run as a non-root
remoteUsersuch asnodeorvscode.
Open the Dev Container template and ask the workspace to "switch this to a Python 3.12 base image" or "forward port 5432 for Postgres", then diff the result.