All posts
5 min read

What Is a Dev Container? devcontainer.json Explained

A dev container defines your development environment as code — the tools, versions, services, and settings a project needs — so it runs the same on every machine and in the cloud. What goes in devcontainer.json, how it differs from a Dockerfile, and where it falls short.

cloud developmentdockertoolingdeveloper experience

"It works on my machine" is usually true. The problem is that everyone's machine is different: a different Node version, a Postgres that's two majors behind, a global package someone installed three years ago and forgot about.

A dev container fixes that by describing the development environment in a file that lives in the repository. Open the project and the tools, versions, services, and editor settings it needs are built for you, the same way for everyone. It's the configuration format behind GitHub Codespaces, and it works locally with VS Code and a range of other tools.

The short definition

A dev container is a Docker container configured specifically for developing a project, described by a devcontainer.json file in a .devcontainer/ folder. The file says which image to start from, what to install, which ports to expose, which editor extensions to load, and what to run once the container starts.

The format is an open specification (the Development Container Specification), which is why the same file works across several tools rather than just one.

A minimal example

{
  "name": "billing-api",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:22",
  "features": {
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "forwardPorts": [3000],
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
    }
  }
}

In order:

  • image — the base container. Prebuilt images exist for most languages.
  • features — reusable add-ons that install common tools (the GitHub CLI, Docker, language runtimes) without you writing install scripts.
  • forwardPorts — ports inside the container that should be reachable from your browser.
  • postCreateCommand — runs once after the container is created. Usually dependency installation.
  • customizations — tool-specific settings, such as VS Code extensions.

Adding a database

Real projects need services. For that, point the dev container at a Docker Compose file:

{
  "name": "billing-api",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspaces/billing-api",
  "forwardPorts": [3000, 5432]
}
# .devcontainer/docker-compose.yml
services:
  app:
    image: mcr.microsoft.com/devcontainers/typescript-node:22
    volumes:
      - ..:/workspaces/billing-api:cached
    command: sleep infinity
    environment:
      DATABASE_URL: postgres://dev:dev@db:5432/billing
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
      POSTGRES_DB: billing
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

Now opening the project starts the app container and a Postgres container, wired together, with the database persisted in a named volume.

Dev container vs Dockerfile

People often ask why not just use the project's Dockerfile. They answer different questions:

Production Dockerfile Dev container
Purpose Run the app Work on the app
Contents Only what the app needs at runtime Compilers, debuggers, linters, CLIs, test tools
Size As small as possible Whatever's useful
Source code Copied in at build time Mounted, so edits appear instantly
User Minimal service user A developer user with a shell

You can build a dev container from a Dockerfile (there's a build property for it), and it's common to share a base between the two. But a good production image is deliberately missing most of what you want while developing.

Where dev containers run

  • Locally, via VS Code's Dev Containers extension or the devcontainer CLI, on top of Docker.
  • In GitHub Codespaces, which builds the container on a cloud machine and connects your editor to it.
  • In other cloud development environments that read the same specification — several of the tools we cover in GitHub Codespaces Alternatives do.

That portability is the main reason to write one: the same file gives a new hire a working environment locally and gives a cloud tool a working environment remotely.

What dev containers are great at

  • Onboarding. Clone, open, wait a few minutes, start working. No wiki page of setup steps.
  • Version consistency. Everyone runs the same runtime and the same database major version.
  • Isolation from your machine. Project tools don't pollute your global environment, and one project's requirements don't conflict with another's.
  • A contract for agents. A coding agent dropped into a dev container gets the same working environment a human would, rather than guessing at setup.

Where they fall short

Honest limits worth knowing before you commit:

  • Build time. A fresh build with dependencies can take minutes. Prebuilds and image caching help, but a cold start is a real cost — especially for tools that recreate the container often.
  • Docker on your laptop. Locally, you're running Docker, with its memory overhead and — on macOS and Windows — a virtual machine underneath and slower file access on mounted volumes.
  • State is your job. Delete the container and anything not in the mounted source folder or a named volume is gone. Database data persists only if you set up a volume for it.
  • It stops at the edge of development. A dev container describes where you write code. It says nothing about where the app runs for real users, how it gets a domain, or where production data lives. Those remain separate problems.
  • Root inside the container. Most dev container images give you root or sudo. That's convenient, and it's why they're a poor security boundary for untrusted code on their own.

Should you add one?

If your project takes more than a few commands to set up, or has more than one person working on it, yes. It's an afternoon of work that pays for itself the first time someone new joins — or the first time you try to run the project on a new laptop.

If your team is weighing local development against cloud environments more broadly, we compare the trade-offs in Cloud Development Environments vs Local Setup.


EasySpawn builds workspaces from stack-specific templates, runs them in isolated containers, and keeps project data on persistent storage — with a managed database, SSL, and a live URL attached, so the environment you develop in is also the one that runs. See how it works or join the waitlist.

Related: Cloud Development Environments vs Local Setup · GitHub Codespaces Alternatives in 2026

Keep reading