Dev Containers: one environment for the whole team
New developers lose days to setup, and 'works on my machine' never dies. A devcontainer.json file gives everyone the same tools, versions and services.
Every project has a setup document, and it is always slightly out of date. A new developer installs the wrong Python version, a different database release and a missing system library, then spends two days finding out why the tests fail. Even on an established team, small differences between laptops produce bugs that only one person can reproduce.
A dev container moves the environment out of the document and into the repository.
What a dev container is
It is a Docker container set up for development, described by one file: .devcontainer/devcontainer.json. The file states which image to use, which tools and editor extensions to install and which commands to run after creation. Your editor opens the project inside that container. The code stays on your disk. The tools run in the container.
{
"name": "shop-api",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "22" }
},
"forwardPorts": [8000],
"postCreateCommand": "pip install -r requirements.txt",
"customizations": {
"vscode": {
"extensions": ["ms-python.python", "charliermarsh.ruff"]
}
}
}
Open the folder, choose Reopen in Container, and a few minutes later you have Python 3.12, Node.js 22, the dependencies and the right extensions. So does everyone else.
Adding a database
Real projects need services. Point the file at a Compose file and the database starts with the environment.
{
"name": "shop-api",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace"
}
The Compose file defines app and, for example, a PostgreSQL service next to it. Nobody installs PostgreSQL locally again.
Where it works
Dev Containers follow an open specification, so the same file is used by:
- VS Code, through the Dev Containers extension,
- GitHub Codespaces, in the browser,
- JetBrains IDEs,
- the
devcontainercommand line tool, which lets CI run in the same environment.
What you gain
- Onboarding in minutes. Clone, reopen in the container, run the tests.
- Fewer "works on my machine" bugs. The machine is the same for everyone.
- A clean laptop. Projects with conflicting versions no longer fight each other.
- Setup that is reviewed. A change to the environment is a pull request like any other.
What it costs
- Docker has to be installed and running, and it uses memory.
- On macOS and Windows, file access between the host and the container is slower. Keeping dependency folders in a Docker volume helps.
- The first build takes time. Prebuilt images solve that for larger teams.
Tips from practice
- Pin versions.
python:3.12is fine.python:latestwill surprise you one morning. - Keep secrets out of the file. Use environment files that are not committed.
- Use the same image in CI, so tests run where the code was written.
- Start small. One image and one
postCreateCommandalready removes most setup pain.
If the images get heavy, the advice in reducing Docker image size applies here as well.