TL;DR:

  • A .devcontainer/devcontainer.json file defines your entire dev environment as code — OS, runtimes, tools, and extensions — so every team member and CI job gets an identical setup.
  • VS Code Dev Containers has been installed 39 million times; in 2026 it’s a first-class part of the development workflow, not a niche tool.
  • GitHub Codespaces runs the same devcontainer.json in the cloud — letting new contributors start coding in 60 seconds with zero local setup.

“Works on my machine” is a 30-year-old problem. Dev containers fix it by turning your environment definition from tribal knowledge and shell scripts into a versioned file that lives next to your code.

What a Dev Container Actually Is

A dev container is a Docker container configured to be your development environment — editor, tools, language runtimes, environment variables, and all. The configuration lives in .devcontainer/devcontainer.json at the root of your repo.

When you open the repo in VS Code with the Dev Containers extension installed, VS Code asks if you’d like to reopen in the container. Say yes, and you’re in a fully configured environment with your exact versions of Node, Python, Go, or whatever you need — extensions pre-installed, terminal inside the container, port forwarding set up automatically.

When a new developer joins the team, they clone the repo, open it in VS Code, and have a working environment in minutes. No README paragraph that says “install pyenv, then asdf, then nvm, then…”

A Minimal devcontainer.json

{
  "name": "My Project",
  "image": "mcr.microsoft.com/devcontainers/python:3.12",
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "20" },
    "ghcr.io/devcontainers/features/git:1": {}
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "dbaeumer.vscode-eslint"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/usr/local/bin/python"
      }
    }
  },
  "postCreateCommand": "pip install -r requirements.txt && npm install",
  "forwardPorts": [8000, 5432],
  "remoteEnv": {
    "DATABASE_URL": "${localEnv:DATABASE_URL}"
  }
}

That’s it. The image key specifies your base container (Microsoft maintains pre-built images for every major language). features are modular add-ons from the devcontainers/features registry — a clean way to layer tools without writing custom Dockerfiles. postCreateCommand runs once after the container builds. forwardPorts makes those ports available on your host machine.

Features: The Part Most People Miss

Dev Container Features are modular packages that add tools to any base image without touching a Dockerfile. Instead of writing:

RUN apt-get install -y git curl && \
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash && \
    apt-get install -y nodejs

You write:

"features": {
  "ghcr.io/devcontainers/features/node:1": { "version": "20" }
}

There are official features for Node, Python, Go, Rust, Java, Docker-in-Docker, GitHub CLI, kubectl, Terraform, and dozens more. Third-party features are published via OCI registries, so anyone can publish and share environment components.

This composability is one of the biggest quality-of-life improvements over maintaining custom Dockerfiles per project.

Using a Custom Dockerfile

When you need something beyond what features provide — custom OS packages, specific build toolchains, or a non-standard base image — you can point devcontainer.json at a Dockerfile:

{
  "name": "Custom Environment",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  }
}

The Dockerfile lives in .devcontainer/ alongside devcontainer.json. This gives you full control while keeping the environment definition in the repo.

GitHub Codespaces: Cloud Dev Containers

GitHub Codespaces runs your devcontainer.json in Microsoft Azure. Open a repo in Codespaces and you get a browser-based VS Code (or an SSH tunnel to your local VS Code) backed by a cloud VM running your container.

The practical benefit: contributors can submit a pull request in 60 seconds without installing anything. For open-source projects, it dramatically lowers the barrier to contribution. For internal teams, it means developers on new laptops or locked-down corporate machines can contribute immediately.

Codespaces supports machine sizes from 2-core (free tier) to 32-core for compute-intensive workloads. The .devcontainer/devcontainer.json format is identical — your container works locally and in Codespaces without modification.

Secrets (API keys, tokens) can be set as Codespaces secrets in repository or organisation settings and mapped into the container via remoteEnv.

Practical Tips for Teams

Version-pin your features. Instead of "ghcr.io/devcontainers/features/node:1", use the specific release SHA for maximum reproducibility. Otherwise an update to the feature can silently change your environment.

Use a postCreateCommand for your dependency install. Running npm install or pip install at container creation means the container is ready to code immediately on open, not after a manual step.

Add VS Code extensions your team actually uses. Extensions don’t install automatically for team members unless you list them in devcontainer.json. This is the most common “why doesn’t linting work for everyone?” fix.

Commit the devcontainer config, not node_modules. The container build handles dependencies. Don’t try to commit installed dependencies into the container configuration.

For databases, use Docker Compose. devcontainer.json supports a dockerComposeFile property that spins up services (PostgreSQL, Redis, etc.) alongside your dev container. You get a full local stack, containerised, launched with a single click.

In 2026, There’s No Excuse

Dev Containers have been around since 2019. In 2026, with 39 million installations and first-class support from VS Code, JetBrains (via the Dev Containers plugin), and GitHub Codespaces, there’s no good reason for a team to still be on-boarding developers with a 20-step README.

The investment is a few hours to write a devcontainer.json. The return is every developer having the same environment — including CI — from that point on. It’s one of the highest-leverage productivity improvements a team can make, and it compounds as the team grows.