Skip to content
Dev Tools Intermediate Tutorial

Ship a Reproducible Dev Environment with VS Code Dev Containers

Set up a Docker-backed .devcontainer folder so every team member gets the same Node version, extensions, and system packages the moment they open the project.

Priya Nair
Priya Nair
AI & Developer Experience Writer · Jun 23, 2026 · 7 min read
Ship a Reproducible Dev Environment with VS Code Dev Containers

What You'll Build

You'll add a .devcontainer folder to an existing project and configure a Docker-backed development environment that VS Code (and GitHub Codespaces) can start with a single command. Anyone who clones the repo gets the same Node version, system packages, and editor extensions without touching their local machine.

Prerequisites

  • VS Code 1.74 or later with the Dev Containers extension (ms-vscode-remote.remote-containers) installed
  • Docker Desktop 4.x on macOS or Windows, or Docker Engine 20.10+ on Linux. Apple Silicon users: Docker Desktop handles ARM64 natively; prefer ARM-native images where available.
  • A project folder with a package.json (this tutorial uses Node 20; the pattern works for any runtime)
  • Comfortable with basic Docker concepts: images, containers, layers

1. Create the .devcontainer Folder

At your project root:

mkdir .devcontainer

Everything VS Code needs lives here. The only required file is devcontainer.json.

2. Write devcontainer.json

Create .devcontainer/devcontainer.json:

{
  "name": "Node.js 20",
  "image": "mcr.microsoft.com/devcontainers/node:1-20-bookworm",
  "features": {
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      }
    }
  },
  "forwardPorts": [3000],
  "postCreateCommand": "npm install",
  "remoteUser": "node"
}
Field What it does
image Pre-built base image from Microsoft's devcontainers registry
features Installs extra tools without a custom Dockerfile
customizations.vscode.extensions Extensions auto-installed inside the container
forwardPorts Maps container port 3000 to your localhost
postCreateCommand Runs once after the container starts (installs deps here)
remoteUser Non-root user VS Code connects as (node is built into this image)

3. Add a Custom Dockerfile When You Need More Control

The image field covers most cases. When you need specific system packages, swap it for a build block pointing at a custom Dockerfile.

Create .devcontainer/Dockerfile:

FROM mcr.microsoft.com/devcontainers/node:1-20-bookworm

RUN apt-get update && apt-get install -y --no-install-recommends \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

Then replace the entire contents of .devcontainer/devcontainer.json with the file below. The only change from Step 2 is that "image" is removed and a "build" block takes its place; all other fields are preserved:

{
  "name": "Node.js 20",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  },
  "features": {
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      }
    }
  },
  "forwardPorts": [3000],
  "postCreateCommand": "npm install",
  "remoteUser": "node"
}

Setting "context": ".." points Docker at the project root (one level up from .devcontainer/), which is needed if your Dockerfile ever copies project files during the build.

4. Open the Folder in the Container

With Docker running, open the Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows/Linux).

If the project is already open in VS Code (the common case), run:

Dev Containers: Reopen in Container

If you haven't opened the folder yet, use:

Dev Containers: Open Folder in Container

VS Code will pull the base image (cached after the first run), apply features, mount your project at /workspaces/<project-name>, install the listed extensions inside the container, and run postCreateCommand. The status bar at the bottom-left turns blue and shows the container name when you're connected.

After editing devcontainer.json, apply changes with:

Dev Containers: Rebuild Container

5. Verify It Works

Open the integrated terminal (Ctrl+`). You're now inside the container:

node --version
# v20.x.x

gh --version
# gh version 2.x.x

whoami
# node

Project files are live-mounted, so edits in VS Code sync instantly without a rebuild. Start your app:

npm run dev

VS Code auto-forwards port 3000. Open http://localhost:3000 in your host browser.

Using with GitHub Codespaces

Push .devcontainer/ to your repo. On GitHub, click Code > Codespaces > Create codespace on main. GitHub spins up the identical environment in the cloud with no local Docker required. The spec is the same file; nothing extra to configure.

Troubleshooting

"Cannot connect to the Docker daemon" Docker Desktop isn't running. On Linux, your user may not be in the docker group. Fix it with sudo usermod -aG docker $USER, then log out and back in.

Extension won't install inside the container Some extensions are marked local-only by their publishers and can't run remotely. Check the extension's marketplace page under the "Remote" section for compatibility. You can still install those extensions on your local VS Code instance; they'll apply to the host UI.

Port isn't reachable on the host Confirm forwardPorts is set and you've rebuilt after changing it. As a fallback, open the Ports panel (View > Open View > Ports) and forward the port manually from there.

Image pull fails on Apple Silicon Some older images lack a linux/arm64 manifest. Add "runArgs": ["--platform=linux/amd64"] to devcontainer.json to force x86 emulation via Rosetta, or find an ARM-native alternative image.

Next Steps

  • Add a database service by switching to Docker Compose: replace image with dockerComposeFile and service in devcontainer.json. The official Compose-based dev containers guide walks through the full setup.
  • Browse available Features at containers.dev/features; Python, Go, Rust, AWS CLI, and dozens more are ready to drop in.
  • Pin feature versions for team stability: "ghcr.io/devcontainers/features/github-cli:1.0.5": {} prevents unexpected updates from breaking your environment.
  • Sync your dotfiles automatically by setting Dev > Containers: Dotfiles Repository in VS Code settings. Dev Containers will clone and run your dotfiles installer in every new container.
Priya Nair
Written by
Priya Nair · AI & Developer Experience Writer

Priya covers AI frameworks, developer productivity tooling, and the startup ecosystem across South and Southeast Asia, bringing a researcher's rigour and a practitioner's empathy to every story. She is deeply sceptical of benchmarks and asks hard questions so her readers don't have to.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading