devcontainer.json is the config file for the devcontainers spec, describing a reproducible dev environment as code. Tools that read it — VS Code’s Dev Containers extension, the devcontainer CLI, devpod — all build the same environment from the same file, regardless of provider.
If a repo has no
devcontainer.json, devpod auto-detects the language and generates a default one for you.
Location
<repo-root>/.devcontainer/devcontainer.json
# or for a single-file setup:
<repo-root>/.devcontainer.json
Minimal Example
{
"name": "<workspace-name>",
"image": "mcr.microsoft.com/devcontainers/python:3"
}Building from a Dockerfile Instead of an Image
dockerfile and context are resolved relative to the folder containing devcontainer.json, not the repo root. For the default .devcontainer/devcontainer.json layout that means ".." to reach the repo root — giving the Dockerfile access to the whole project instead of just the .devcontainer/ folder. For the single-file .devcontainer.json variant at the repo root, that same folder is already the repo root, so context should be "." instead.
{
"name": "<workspace-name>",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
}
}Installing a Package with postCreateCommand
postCreateCommand runs once, right after the container is created — a quick way to install a package without writing a Dockerfile:
{
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y <package>"
}This only runs at creation. Installing a package by hand inside a running workspace (e.g. SSH’ing in and running
apt install) is untracked — it’s lost on the next--recreateor image rebuild. Anything that needs to survive a rebuild belongs inpostCreateCommand, a DockerfileRUN, or a feature.
For apt packages specifically, prefer the apt-packages feature over postCreateCommand — it’s declarative and composes with other features’ install ordering, instead of a raw shell command:
{
"features": {
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
"packages": "<package>,<package>"
}
}
}Key Fields
image/build— base image to pull, or a Dockerfile/Containerfile to build (mutually exclusive).features— reusable, composable install scripts layered on top of the base image without hand-writing Dockerfile steps. Each key’s value is that feature’s options object ({}uses its defaults); available options are feature-specific, documented in its own README. Browse available features at containers.dev/features.forwardPorts— ports to forward automatically once the container starts (e.g.[3000, 5432]).postCreateCommand— lifecycle hook: shell command run once, after the container is created (e.g. installing dependencies).postStartCommand— lifecycle hook: shell command run every time the container starts (create or restart).customizations.vscode.extensions— list of extension IDs to auto-install in the container.remoteUser— user the tooling connects as inside the container (defaults to the image’s default user).mounts— extra bind mounts beyond the automatic repo mount (e.g. SSH keys, Docker socket).
Example with Features and Hooks
{
"name": "<workspace-name>",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"forwardPorts": [3000],
"postCreateCommand": "npm install",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint"]
}
}
}