DevToolBoxGRÁTIS
Blog

Dev Containers Complete Guide 2026: Reproducible Development Environments with devcontainer.json

26 min readby DevToolBox Team

TL;DR

Dev Containers define your entire development environment as code using a devcontainer.json file, ensuring every developer gets an identical, reproducible setup with a single click. In 2026, Dev Containers are natively supported in VS Code, JetBrains IDEs, GitHub Codespaces, and CI/CD pipelines. They eliminate "works on my machine" issues by containerizing editor settings, extensions, tools, runtimes, databases, and services into a portable, version-controlled configuration.

Key Takeaways

  • devcontainer.json is an open specification supported by VS Code, JetBrains, GitHub Codespaces, and CLI tools
  • Pre-built images from mcr.microsoft.com/devcontainers cover most languages out of the box
  • The Features system lets you compose tools without writing Dockerfiles
  • Docker Compose integration enables multi-service dev environments with databases and caches
  • GitHub Codespaces provides cloud-hosted dev containers accessible from any browser
  • Lifecycle commands automate dependency installation and service startup
  • GPU passthrough enables ML/AI development inside containers with CUDA support
  • The devcontainer CLI enables CI/CD pipelines to use the same environment developers use locally

Table of Contents

  1. What Are Dev Containers
  2. Dev Containers vs Docker Compose vs Vagrant vs Nix
  3. devcontainer.json Specification
  4. VS Code Setup
  5. GitHub Codespaces
  6. Pre-built Images
  7. Custom Dockerfile
  8. Features System
  9. Docker Compose Multi-Service
  10. Port Forwarding
  11. Environment Variables and Secrets
  12. VS Code Extensions and Settings
  13. Lifecycle Commands
  14. Volumes and Mounts
  15. GPU Support for ML/AI
  16. Multi-Root Workspaces
  17. Templates and Community Features
  18. JetBrains Support
  19. Performance Optimization
  20. Team Onboarding
  21. CI/CD Integration
  22. Security Best Practices
  23. Common Language Patterns
  24. Troubleshooting

The "works on my machine" problem has plagued software teams for decades. Different operating systems, tool versions, and system configurations cause builds to fail and onboarding to take days. Dev Containers solve this by defining your entire development environment as a single JSON file in your repository. In 2026, the specification has matured into an open standard supported by every major editor and cloud platform.

What Are Dev Containers and Why They Matter

A Dev Container is a Docker container specifically configured for development. Unlike production containers, dev containers include editors, debuggers, language runtimes, linters, and every tool a developer needs. The configuration lives in .devcontainer/devcontainer.json, making it version-controlled and shared across the team.

Why Dev Containers Matter in 2026

  • Zero onboarding friction: new team members get a working environment in minutes
  • Reproducible builds: everyone runs the exact same tools and versions
  • OS independence: same devcontainer.json works on macOS, Windows, and Linux
  • Cloud-ready: runs locally or instantly in GitHub Codespaces from a browser
  • Open specification: supported by VS Code, JetBrains, DevPod, and CLI tools
  • CI/CD parity: test and build inside the same container developers use

Dev Containers vs Docker Compose vs Vagrant vs Nix

Dev Containers are not the only solution for reproducible environments. Here is how they compare to other popular approaches.

FeatureDev ContainersDocker ComposeVagrantNix
PurposeDev environment as codeMulti-container orchestrationVM-based dev environmentsReproducible packages
Editor IntegrationVS Code, JetBrains nativeNoneSSH onlydirenv + plugins
Cloud SupportCodespaces, DevPodNoneNoneNone
Startup SpeedSeconds (cached)SecondsMinutes (VM)Seconds (cached)
Resource UsageLow (containers)Low (containers)High (full VM)Minimal (host)
Learning CurveLow-MediumMediumMediumHigh
Multi-ServiceVia Docker ComposeNativeMultiple VMsNixOS containers
OS IsolationLinux containerLinux containerFull VM (any OS)None (host)

devcontainer.json Specification and Structure

The devcontainer.json file is the heart of every Dev Container. It follows the open specification at containers.dev and lives in .devcontainer/devcontainer.json.

// .devcontainer/devcontainer.json — full reference
{
  "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
  // OR: "build": { "dockerfile": "Dockerfile", "context": ".." },
  // OR: "dockerComposeFile": "docker-compose.yml", "service": "app",

  "name": "My Project Dev",

  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "20" },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },

  "forwardPorts": [3000, 5432, 6379],
  "portsAttributes": {
    "3000": { "label": "App", "onAutoForward": "notify" },
    "5432": { "label": "PostgreSQL", "onAutoForward": "silent" }
  },

  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
      "settings": { "editor.formatOnSave": true }
    }
  },

  "containerEnv": { "NODE_ENV": "development" },
  "remoteEnv": { "LOCAL_USER": "\${localEnv:USER}" },

  "postCreateCommand": "npm install && npx prisma migrate dev",
  "postStartCommand": "npm run dev",
  "remoteUser": "node",

  "mounts": [
    "source=node-modules,target=/workspace/node_modules,type=volume",
    "source=\${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,readonly"
  ],
  "runArgs": ["--memory=4g", "--cpus=2"]
}

VS Code Dev Containers Extension Setup

VS Code provides first-class support through the Dev Containers extension. After installing it, VS Code detects .devcontainer/devcontainer.json and offers to reopen the folder inside a container.

The extension requires Docker Desktop (or Docker Engine on Linux) to be installed and running. On macOS and Windows, Docker Desktop provides the Docker daemon and a Linux VM for running containers. On Linux, Docker Engine runs natively with the best performance.

# Step 1: Install prerequisites
# macOS/Windows: Install Docker Desktop from docker.com
# Linux: Install Docker Engine
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Step 2: Install the Dev Containers extension
code --install-extension ms-vscode-remote.remote-containers

# Step 3: Verify Docker is running
docker --version    # Docker 24+ recommended
docker info          # Verify Docker daemon is active

# Step 4: Open project — VS Code detects devcontainer.json
code /path/to/project
# Click "Reopen in Container" notification
# Or: Ctrl+Shift+P > "Dev Containers: Reopen in Container"

# Key commands (Command Palette):
# Dev Containers: Rebuild Container         — after config changes
# Dev Containers: Rebuild Without Cache     — force clean build
# Dev Containers: Reopen Folder Locally     — exit container mode
# Dev Containers: Show Container Log        — debug build issues
# Dev Containers: Open Folder in Container  — open any folder
# Dev Containers: Clone Repository in Container Volume — faster I/O

GitHub Codespaces Integration

GitHub Codespaces provides cloud-hosted dev containers that launch from any GitHub repository. It reads your devcontainer.json and builds the environment on a cloud VM, accessible via browser or local VS Code.

# Create a Codespace:
# 1. Go to repo on GitHub > Code > Codespaces > Create codespace

# Machine types (2026):
# 2-core / 8GB   — free tier (120 hrs/month)
# 4-core / 16GB  — \$0.36/hr
# 8-core / 32GB  — \$0.72/hr
# 16-core / 64GB — \$1.44/hr
# 32-core / 128GB — \$2.88/hr (GPU available)

# Prebuilds: Settings > Codespaces > Set up prebuild
# Reduces Codespace startup to seconds

# Secrets: GitHub > Settings > Codespaces > Secrets
# Available as env vars, never committed to source

Pre-built Dev Container Images

Microsoft provides pre-built images at mcr.microsoft.com/devcontainers, optimized for dev container use with common tools and regular security updates.

ImageIncludesSize
devcontainers/base:ubuntuGit, curl, zsh, utilities~350MB
devcontainers/typescript-node:20Node.js 20, npm, yarn, TypeScript~650MB
devcontainers/python:3.12Python 3.12, pip, venv~600MB
devcontainers/go:1.22Go 1.22, gopls, delve~800MB
devcontainers/rust:1Rust, cargo, clippy~1.2GB
devcontainers/java:21JDK 21, Maven, Gradle~900MB
devcontainers/universal:2Node, Python, Java, Go, .NET, PHP, Ruby~6GB

Custom Dockerfile for Dev Containers

When pre-built images do not cover your needs, provide a custom Dockerfile. The devcontainer.json references it and VS Code builds the image before starting the container.

When writing a Dockerfile for dev containers, start from a devcontainers base image whenever possible. These images already include common utilities like git, curl, sudo, and zsh. Then add your project-specific system packages, language runtimes, and global tools. Keep the image focused on development needs, not production requirements.

# .devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/base:ubuntu

# Install system dependencies for native compilation
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    libssl-dev \
    pkg-config \
    protobuf-compiler \
    graphviz \
    && rm -rf /var/lib/apt/lists/*

# Install Node.js 20
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
    && apt-get install -y nodejs

# Install global npm packages
RUN npm install -g pnpm@9 turbo@2 tsx

# Install Rust toolchain (optional for projects using native modules)
RUN curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | \
    sh -s -- -y --default-toolchain stable
ENV PATH="/root/.cargo/bin:\${PATH}"

# Create non-root user workspace
WORKDIR /workspace
// .devcontainer/devcontainer.json with custom Dockerfile
{
  "name": "Custom Dev Environment",
  "build": {
    "dockerfile": "Dockerfile",
    "context": "..",
    "args": {
      "NODE_VERSION": "20",
      "VARIANT": "bookworm"
    },
    "cacheFrom": "ghcr.io/myorg/devcontainer:latest"
  },
  "remoteUser": "vscode",
  "postCreateCommand": "npm install"
}

Features System: Installing Tools and Languages

Dev Container Features are composable units of installation code. Instead of writing Dockerfile RUN commands, add Features to devcontainer.json. They handle installation, version pinning, and cross-platform compatibility.

Features are distributed as OCI artifacts (container images) and follow a well-defined installation protocol. Each Feature includes a devcontainer-feature.json manifest and an install.sh script. The official Features repository at github.com/devcontainers/features covers the most common tools, while community Features at github.com/devcontainers-contrib/features extend coverage to hundreds of additional tools.

// Popular Dev Container Features (2026)
{
  "features": {
    // Language runtimes
    "ghcr.io/devcontainers/features/node:1": { "version": "20" },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
    "ghcr.io/devcontainers/features/go:1": { "version": "1.22" },
    "ghcr.io/devcontainers/features/rust:1": { "version": "stable" },
    "ghcr.io/devcontainers/features/java:1": { "version": "21" },

    // Container and orchestration tools
    "ghcr.io/devcontainers/features/docker-in-docker:2": {},
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
    "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {},

    // Cloud CLIs
    "ghcr.io/devcontainers/features/aws-cli:1": {},
    "ghcr.io/devcontainers/features/azure-cli:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {},

    // Database clients
    "ghcr.io/devcontainers/features/postgresql-client:1": {},

    // Shell and utilities
    "ghcr.io/devcontainers/features/common-utils:2": {
      "installZsh": true,
      "installOhMyZsh": true
    },
    "ghcr.io/devcontainers/features/sshd:1": {},
    "ghcr.io/devcontainers/features/git-lfs:1": {}
  }
}

Docker Compose Multi-Service Setup

For applications needing databases, caches, or other services, Dev Containers integrate with Docker Compose. Define services in docker-compose.yml and reference it from devcontainer.json.

The key pattern is to use sleep infinity as the command for the main development service. This keeps the container running so VS Code can attach to it. The actual development server is started by the developer or by postStartCommand, not by Docker Compose directly. Other services like databases run their normal entrypoints.

# .devcontainer/docker-compose.yml
services:
  app:
    build:
      context: ..
      dockerfile: .devcontainer/Dockerfile
    volumes:
      - ..:/workspace:cached
      - node-modules:/workspace/node_modules
    command: sleep infinity
    ports:
      - "3000:3000"
      - "9229:9229"   # Node.js debugger
    environment:
      DATABASE_URL: postgresql://postgres:postgres@db:5432/myapp
      REDIS_URL: redis://redis:6379
      NODE_ENV: development
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  node-modules:
  postgres-data:
  redis-data:
// devcontainer.json with Docker Compose
{
  "name": "Full Stack Dev",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspace",
  "forwardPorts": [3000, 5432, 6379],
  "postCreateCommand": "npm install && npx prisma migrate dev --name init",
  "customizations": { "vscode": {
    "extensions": ["prisma.prisma", "dbaeumer.vscode-eslint"]
  }}
}

Port Forwarding and Networking

Dev containers automatically forward ports from container to host. Configure forwarding behavior in devcontainer.json to control which ports are forwarded, their labels, and visibility.

{
  "forwardPorts": [3000, 5432, 6379, 8080],
  "portsAttributes": {
    "3000": { "label": "Frontend", "onAutoForward": "openBrowser" },
    "5432": { "label": "PostgreSQL", "onAutoForward": "silent" },
    "6379": { "label": "Redis", "onAutoForward": "ignore" },
    "8080": { "label": "API", "onAutoForward": "notify", "requireLocalPort": true }
  },
  "otherPortsAttributes": { "onAutoForward": "silent" }
}

Environment Variables and Secrets

Dev containers support multiple ways to inject environment variables: inline in devcontainer.json, from .env files, or via GitHub Codespaces secrets for sensitive values.

// Method 1: Inline
{ "containerEnv": { "NODE_ENV": "development", "LOG_LEVEL": "debug" },
  "remoteEnv": { "LOCAL_USER": "\${localEnv:USER}" } }

// Method 2: .env file (add to .gitignore!)
// { "runArgs": ["--env-file", ".devcontainer/.env"] }
// .devcontainer/.env:
// DATABASE_URL=postgresql://user:pass@db:5432/myapp
// AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE

// Method 3: GitHub Codespaces secrets
// GitHub > Settings > Codespaces > Secrets
// Available as env vars, never committed

VS Code Extensions and Settings in devcontainer.json

Specify which VS Code extensions to install and what editor settings to apply, ensuring every developer has the same linter rules, formatter, and language support.

Extensions listed in the customizations block are installed inside the container, not on the host VS Code. This distinction matters because some extensions need access to language servers, runtimes, or tools that only exist inside the container. Extensions like ESLint need Node.js, Python extensions need the Python interpreter, and database extensions need database clients. By installing them in the container, they automatically have access to everything they need.

// VS Code customizations in devcontainer.json
{
  "customizations": {
    "vscode": {
      "extensions": [
        // Linting and formatting
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "stylelint.vscode-stylelint",

        // Language support
        "ms-python.python",
        "golang.go",
        "rust-lang.rust-analyzer",

        // Database and API tools
        "prisma.prisma",
        "humao.rest-client",

        // Git and collaboration
        "eamodio.gitlens",
        "github.copilot",

        // Testing
        "vitest.explorer"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "editor.tabSize": 2,
        "editor.rulers": [80, 120],
        "typescript.preferences.importModuleSpecifier": "relative",
        "files.trimTrailingWhitespace": true,
        "files.insertFinalNewline": true
      }
    }
  }
}

Post-Create and Post-Start Commands

Lifecycle commands run at specific points during container setup, automating dependency installation, database seeding, or background service startup.

Understanding the lifecycle order is critical for efficient container setup. Commands that should only run once (like initial dependency installation) belong in postCreateCommand, while commands that should run on every restart (like database migrations) belong in postStartCommand. The object syntax allows running multiple commands in parallel, which speeds up container initialization significantly.

{
  // Runs once after the container is first created
  "postCreateCommand": "npm install && npx prisma generate",

  // Runs every time the container starts
  "postStartCommand": "npm run db:migrate",

  // Runs every time VS Code attaches to the container
  "postAttachCommand": "git fetch --all",

  // Runs on rebuild to update dependencies incrementally
  "updateContentCommand": "npm install",

  // Parallel commands (object syntax — runs simultaneously):
  "postCreateCommand": {
    "deps": "npm install",
    "db": "npx prisma migrate dev",
    "seed": "npx prisma db seed",
    "codegen": "npm run codegen"
  },

  // Runs on HOST before the container is built
  "initializeCommand": "echo Starting dev container setup..."
}

// Complete lifecycle order:
// 1. initializeCommand   — runs on HOST before build
// 2. onCreateCommand     — once when container is created
// 3. updateContentCommand — on create and rebuild
// 4. postCreateCommand   — once after container is created
// 5. postStartCommand    — every time container starts
// 6. postAttachCommand   — every time editor attaches

Volumes and Mounts for Persistence

The workspace folder is bind-mounted by default. Add additional mounts for caches, credentials, or data that should persist across container rebuilds.

{
  "mounts": [
    "source=myproject-node-modules,target=/workspace/node_modules,type=volume",
    "source=\${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,readonly",
    "source=\${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind,readonly",
    "source=\${localEnv:HOME}/.aws,target=/home/node/.aws,type=bind,readonly",
    "target=/tmp,type=tmpfs"
  ],
  "workspaceMount": "source=\${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
  "workspaceFolder": "/workspace"
}

GPU Support for ML/AI Development

Dev containers support GPU passthrough for ML/AI work. With NVIDIA Container Toolkit on the host, expose GPUs for training models or running CUDA kernels.

GPU support is particularly valuable for teams working on machine learning models, computer vision, natural language processing, or any CUDA-accelerated workload. The hostRequirements field with gpu set to optional allows the same devcontainer.json to work on both GPU and non-GPU machines, making it versatile across team members with different hardware.

// GPU-enabled dev container for ML/AI development
{
  "image": "mcr.microsoft.com/devcontainers/python:3.12",
  "features": {
    "ghcr.io/devcontainers/features/nvidia-cuda:1": {
      "installCudnn": true,
      "cudaVersion": "12.4",
      "cudnnVersion": "9"
    },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" }
  },
  "runArgs": [
    "--gpus", "all"              // Pass all GPUs to container
    // "--gpus", "device=0"      // Or pass a specific GPU
  ],
  "hostRequirements": {
    "gpu": "optional"            // Works without GPU too
  },
  "postCreateCommand": "pip install torch torchvision transformers datasets",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-toolsai.jupyter",
        "ms-toolsai.vscode-jupyter-slideshow"
      ]
    }
  }
}

// Host prerequisites:
// 1. NVIDIA driver installed on the host machine
// 2. Install NVIDIA Container Toolkit:
//    sudo apt install nvidia-container-toolkit
//    sudo systemctl restart docker
// 3. Verify setup:
//    docker run --gpus all nvidia/cuda:12.4-base nvidia-smi

Multi-Root Workspaces

Dev containers support multi-root workspaces where multiple repositories open in a single VS Code window, useful for microservice architectures.

{
  "name": "Microservices Dev",
  "dockerComposeFile": "docker-compose.yml",
  "service": "workspace",
  "workspaceFolder": "/workspaces",
  "postCreateCommand": {
    "frontend": "cd /workspaces && git clone https://github.com/org/frontend.git",
    "backend": "cd /workspaces && git clone https://github.com/org/backend-api.git"
  }
}

// Or use workspace file .devcontainer/workspace.code-workspace:
// { "folders": [
//   { "path": "/workspaces/frontend", "name": "Frontend" },
//   { "path": "/workspaces/backend-api", "name": "Backend" }
// ]}

Dev Container Templates and Community Features

The specification includes a Templates system providing starter configs for common scenarios, available from official and community repositories.

# Install devcontainer CLI
npm install -g @devcontainers/cli

# Apply a template:
devcontainer templates apply \
  --template-id ghcr.io/devcontainers/templates/typescript-node

# Or via VS Code: Dev Containers: Add Dev Container Configuration Files...

# Community Features (add to "features" in devcontainer.json):
# ghcr.io/devcontainers-contrib/features/bun:1
# ghcr.io/devcontainers-contrib/features/pnpm:2
# ghcr.io/devcontainers-contrib/features/turbo-cli:1
# ghcr.io/devcontainers-contrib/features/act:1        (GitHub Actions local)
# ghcr.io/devcontainers-contrib/features/mkcert:1     (local HTTPS)

JetBrains Dev Container Support

JetBrains IDEs support Dev Containers through the remote development gateway. The IDE reads devcontainer.json, builds the container, and connects via SSH with the full IDE backend inside.

{
  "customizations": {
    "jetbrains": {
      "backend": {
        "productCode": "IU",        // IntelliJ IDEA Ultimate
        // "PS" PhpStorm, "WS" WebStorm, "PY" PyCharm, "GO" GoLand, "CL" CLion
        "plugins": ["org.jetbrains.plugins.go", "com.intellij.database"]
      }
    },
    "vscode": { "extensions": ["dbaeumer.vscode-eslint"] }
  }
}

// Steps: Install JetBrains Gateway > Select "Dev Containers"
// > Choose project > Gateway builds container and connects IDE

Performance Optimization

Dev containers can feel slow if misconfigured, especially on macOS where Docker runs in a Linux VM. Here are the most impactful optimizations.

  • Use named Docker volumes for node_modules and dependency directories instead of bind mounts
  • Enable Docker BuildKit for faster builds with better layer caching
  • Use pre-built images instead of building from Dockerfile every time
  • Configure Codespaces prebuilds to pre-compute your dev container image
  • Mount .gitconfig and SSH keys from host instead of regenerating inside the container
  • Set updateContentCommand for incremental dependency updates on rebuild
  • Use the cacheFrom property to leverage Docker registry layer caching
// Performance-optimized devcontainer.json
{
  "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
  "mounts": ["source=proj-nm,target=/workspace/node_modules,type=volume"],
  "workspaceMount": "source=\${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
  "workspaceFolder": "/workspace",
  "cacheFrom": "ghcr.io/myorg/devcontainer-cache:latest",
  "updateContentCommand": "npm install",
  "runArgs": ["--memory=8g", "--cpus=4"],
  "hostRequirements": { "cpus": 4, "memory": "8gb", "storage": "32gb" }
}

Team Onboarding with Dev Containers

Dev containers transform onboarding from a multi-day ordeal into a single-click operation. Combined with GitHub Codespaces, onboarding requires zero local setup.

The traditional onboarding process involves installing the correct versions of multiple language runtimes, setting up databases, configuring environment variables, installing editor plugins, and troubleshooting platform-specific issues. With Dev Containers, all of this is encoded in the repository. The onboarding documentation reduces from pages of instructions to a single step: open the project in VS Code and accept the container prompt.

# Option A: Local development (VS Code)
# 1. Install Docker Desktop and VS Code
# 2. Install Dev Containers extension
# 3. Clone the repository and open in VS Code:
git clone https://github.com/myorg/myproject.git
code myproject
# 4. Click "Reopen in Container" when prompted
# 5. Done — full environment ready in 2-5 minutes

# Option B: Cloud development (GitHub Codespaces)
# 1. Go to github.com/myorg/myproject
# 2. Click Code > Codespaces > Create codespace
# 3. Done — browser-based VS Code, zero local install needed

# What devcontainer.json provides automatically:
# - Correct Node.js / Python / Go / Rust version
# - All required VS Code extensions pre-installed
# - Database (PostgreSQL) running and seeded
# - Redis cache running
# - Environment variables configured
# - Git hooks and linters configured
# - All dependencies installed (npm/pip/cargo)
# - Ports forwarded for local testing
# - Shared editor settings (formatting, tab size, rulers)

CI/CD with Dev Containers

The devcontainer CLI and GitHub Actions let you build and test inside the exact same environment developers use locally, eliminating CI/CD environment drift.

The devcontainers/ci GitHub Action handles building the dev container image, starting the container, running your commands inside it, and optionally pushing the built image to a container registry for caching. This means your CI pipeline uses the exact same Node.js version, Python packages, system libraries, and tooling that developers use locally. No more debugging CI-only failures caused by environment differences.

# .github/workflows/ci.yml — Dev Container CI/CD
name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build and run dev container
        uses: devcontainers/ci@v0.3
        with:
          # Run all checks inside the dev container
          runCmd: |
            npm run lint
            npm run type-check
            npm run test
            npm run build

          # Cache the built image in a container registry
          imageName: ghcr.io/myorg/devcontainer-cache
          cacheFrom: ghcr.io/myorg/devcontainer-cache
          push: filter
          refFilterForPush: refs/heads/main

# Standalone CLI usage (local or any CI system):
# npm install -g @devcontainers/cli
# devcontainer build --workspace-folder .
# devcontainer up --workspace-folder .
# devcontainer exec --workspace-folder . npm test
# devcontainer exec --workspace-folder . npm run build

Security Best Practices

Dev containers run arbitrary code on your machine, so security matters. Follow these practices.

  • Never run containers as root — use the remoteUser field
  • Pin image versions with SHA digests in critical environments
  • Store secrets in Codespaces secrets or local env vars, never in devcontainer.json
  • Audit third-party Features before adding them
  • Use Docker Content Trust to verify image signatures
  • Avoid --privileged unless absolutely necessary
  • Keep base images updated with regular rebuilds
  • Use .env files in .gitignore to prevent secret commits

Common Patterns by Language

Production-ready devcontainer.json examples for the most popular languages in 2026.

Node.js / TypeScript

{
  "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
  "features": { "ghcr.io/devcontainers-contrib/features/pnpm:2": {} },
  "forwardPorts": [3000, 5173],
  "postCreateCommand": "pnpm install",
  "mounts": ["source=nm,target=/workspace/node_modules,type=volume"],
  "customizations": { "vscode": {
    "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
  }}
}

Python

{
  "image": "mcr.microsoft.com/devcontainers/python:3.12",
  "forwardPorts": [8000],
  "postCreateCommand": "pip install -r requirements.txt",
  "customizations": { "vscode": {
    "extensions": ["ms-python.python", "charliermarsh.ruff"],
    "settings": { "python.defaultInterpreterPath": "/usr/local/bin/python" }
  }}
}

Go

{
  "image": "mcr.microsoft.com/devcontainers/go:1.22",
  "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {} },
  "forwardPorts": [8080],
  "postCreateCommand": "go mod download",
  "mounts": ["source=gomod,target=/go/pkg/mod,type=volume"],
  "customizations": { "vscode": { "extensions": ["golang.go"] }}
}

Rust

{
  "image": "mcr.microsoft.com/devcontainers/rust:1",
  "forwardPorts": [8080],
  "postCreateCommand": "cargo build",
  "mounts": [
    "source=cargo-reg,target=/usr/local/cargo/registry,type=volume",
    "source=cargo-target,target=/workspace/target,type=volume"
  ],
  "customizations": { "vscode": {
    "extensions": ["rust-lang.rust-analyzer", "vadimcn.vscode-lldb"],
    "settings": { "rust-analyzer.checkOnSave.command": "clippy" }
  }}
}

Java

{
  "image": "mcr.microsoft.com/devcontainers/java:21",
  "features": { "ghcr.io/devcontainers/features/java:1": {
    "version": "21", "installMaven": true, "installGradle": true } },
  "forwardPorts": [8080, 5005],
  "postCreateCommand": "./mvnw dependency:resolve",
  "mounts": ["source=m2,target=/home/vscode/.m2/repository,type=volume"],
  "customizations": { "vscode": {
    "extensions": ["vscjava.vscode-java-pack", "vmware.vscode-spring-boot"]
  }}
}

Troubleshooting Guide

Here are the most common Dev Container issues and solutions.

IssueCauseSolution
Container fails to buildDockerfile error or missing imageCheck Container Log via Command Palette. Verify image name.
Slow file I/O on macOSBind mounts through Linux VMUse named volumes for node_modules. Set consistency: cached.
Port already in useHost port conflictStop conflicting service or change port in devcontainer.json.
Extensions not installingPlatform/architecture mismatchCheck extension supports Linux containers.
Git SSH auth failsSSH agent not forwardedRun ssh-add on host. VS Code forwards agent automatically.
Out of disk spaceDocker images and cacheRun docker system prune. Increase Docker disk allocation.
Features fail to installNetwork issues or version conflictCheck connectivity. Pin Feature versions.
Container keeps restartingpostStartCommand exits with errorCheck terminal output. Use background execution for long commands.
# Common troubleshooting commands:

# View dev container build logs
# VS Code: Command Palette > Dev Containers: Show Container Log

# List running dev containers
docker ps --filter "label=devcontainer.local_folder"

# Inspect container configuration
docker inspect <container-id> | jq ".[0].Config"

# Check Docker resource usage
docker system df

# Clean up unused images, containers, and volumes
docker system prune -a --volumes

# Force rebuild without cache
# VS Code: Command Palette > Dev Containers: Rebuild Without Cache

# Test devcontainer.json validity with CLI:
npm install -g @devcontainers/cli
devcontainer build --workspace-folder .
devcontainer up --workspace-folder .

# Check container logs for errors:
docker logs <container-id> --tail 100

# Enter the container manually for debugging:
docker exec -it <container-id> /bin/bash

Frequently Asked Questions

What is the difference between Dev Containers and Docker Compose?

Docker Compose defines multi-container stacks, while Dev Containers define development environments with editor settings, extensions, and tools on top. Dev Containers can use Docker Compose under the hood for multi-service setups.

Do Dev Containers work without VS Code?

Yes. JetBrains IDEs support devcontainer.json natively. The devcontainer CLI works from any terminal. DevPod and GitHub Codespaces also support the specification.

How do Dev Containers affect build performance?

Initial build can take minutes depending on image and Features. Subsequent starts are fast due to Docker layer caching. On macOS, use named volumes for dependencies to avoid file system bottlenecks.

Can I use Dev Containers for monorepos?

Yes. Place devcontainer.json at the repo root. For multi-repo projects, use Docker Compose to define all services and attach VS Code to the primary service.

How do I share SSH keys and Git config with the container?

VS Code automatically forwards your local SSH agent. Git credentials are forwarded via the built-in credential helper. For additional credentials, use mounts or environment variables.

Are Dev Containers free?

The specification, VS Code, and extension are free. Local containers use your Docker installation at no cost. GitHub Codespaces has a free tier (120 core-hours/month) with paid tiers for heavier usage.

Can Dev Containers access the host file system?

The workspace folder is mounted by default. Add mounts in devcontainer.json for other directories. Ports are forwarded between container and host automatically.

How do I update a Dev Container after changes?

Run Dev Containers: Rebuild Container in VS Code. For clean rebuilds, use Rebuild Without Cache. In Codespaces, rebuild from the command palette or create a new Codespace.

Conclusion

Dev Containers are the most practical solution to reproducible development environments in 2026. By defining your setup as a devcontainer.json committed to your repository, you ensure every team member, CI pipeline, and cloud workspace runs the identical environment. Start with a pre-built image, add Features for your tools, and iterate from there.

Getting Started Checklist

  1. Install Docker Desktop and VS Code with the Dev Containers extension
  2. Create a .devcontainer folder in your repository root
  3. Add a devcontainer.json with a pre-built image matching your language
  4. Add Features for additional tools (Docker-in-Docker, AWS CLI, database clients)
  5. Configure VS Code extensions and settings in the customizations block
  6. Set up lifecycle commands for dependency installation and service startup
  7. Add Docker Compose if you need databases or other services
  8. Use named volumes for dependency directories to improve macOS performance
  9. Configure GitHub Codespaces prebuilds for instant cloud environments
  10. Set up CI/CD with the devcontainer CLI or GitHub Actions integration

For additional resources, visit the official Dev Container specification at containers.dev, explore the pre-built images and Features at github.com/devcontainers, and check the VS Code remote development documentation. The ecosystem is growing rapidly with community Features and Templates covering nearly every development scenario.

𝕏 Twitterin LinkedIn
Isso foi útil?

Fique atualizado

Receba dicas de dev e novos ferramentas semanalmente.

Sem spam. Cancele a qualquer momento.

Try These Related Tools

{ }JSON Formatter

Related Articles

Melhores práticas Docker: 20 dicas para containers em produção

Domine Docker com 20 melhores práticas essenciais: builds multi-stage, segurança, otimização de imagens e CI/CD.

Atalhos de teclado do VS Code: Guia completo de produtividade

Domine os atalhos do VS Code para navegacao, edicao, multi-cursor, pesquisa, depuracao e terminal.

Tutorial Docker Compose: Do basico a stacks prontos para producao

Tutorial completo Docker Compose: sintaxe docker-compose.yml, servicos, redes, volumes, variaveis de ambiente, healthchecks e exemplos com Node.js/Python/WordPress.