DevContainers Deep Dive
Complete guide to Development Containers - the specification that powers reproducible, containerized development environments
What are DevContainers?
Development Containers (DevContainers) are a standardized way to define complete development environments as code using Docker containers. The specification, maintained by the Development Containers Spec, allows developers to define their entire development environment - tools, runtimes, extensions, settings - in a single JSON file. The specification has seen broad adoption beyond its VS Code origins and is now supported by VS Code, JetBrains, Zed, GitHub Codespaces, and multiple CDE platforms.
Declarative
Define everything in devcontainer.json
Reproducible
Same environment for every developer
Docker-Based
Built on Docker container technology
How DevContainers Relate to Docker
Standard Docker
- Runs production workloads
- Minimal images for efficiency
- Usually stateless
- Optimized for runtime
DevContainers
- Runs development environments
- Full tooling (git, debuggers, linters)
- Persistent workspaces
- Optimized for development
DevContainer Anatomy
The devcontainer.json File
{
"name": "My Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
// Or build from Dockerfile
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
// Features - modular add-ons
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
// Lifecycle scripts
"postCreateCommand": "npm install",
"postStartCommand": "npm run dev",
// Editor customizations (VS Code, JetBrains, Zed, and more)
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"settings": {
"editor.formatOnSave": true
}
}
},
// Port forwarding
"forwardPorts": [3000, 5432],
// Environment variables
"containerEnv": {
"NODE_ENV": "development"
},
// Mounts and volumes
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
]
}Dockerfile Integration
You can use pre-built images or define custom Dockerfiles for complete control:
# .devcontainer/Dockerfile
FROM ubuntu:22.04
# Install system dependencies
RUN apt-get update && apt-get install -y \
curl \
git \
build-essential \
python3 \
python3-pip
# Install Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get install -y nodejs
# Create non-root user
ARG USERNAME=developer
RUN useradd -m -s /bin/bash $USERNAME
USER $USERNAME
WORKDIR /home/$USERNAMEDocker Compose Support
For multi-container setups (app + database + cache):
{
"name": "Full Stack",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace"
}services:
app:
build: .
volumes:
- .:/workspace
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: dev
redis:
image: redis:7-alpineFeatures System
Features are self-contained, reusable units of installation code. Think of them as modular add-ons:
devcontainers/features/gitdevcontainers/features/nodedevcontainers/features/pythondevcontainers/features/docker-in-dockerdevcontainers/features/kubectl-helmdevcontainers/features/aws-cliCommon Language Configurations
Click on a language to view its devcontainer.json configuration:
Python / Django
{
"name": "Python Django",
"image": "mcr.microsoft.com/devcontainers/python:3.11-bullseye",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"postCreateCommand": "pip install -r requirements.txt && python manage.py migrate",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"batisteo.vscode-django"
]
}
},
"forwardPorts": [8000],
"containerEnv": {
"DJANGO_SETTINGS_MODULE": "myproject.settings.dev"
}
} Node.js / React
{
"name": "Node.js React",
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postCreateCommand": "npm install",
"postStartCommand": "npm run dev",
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
},
"forwardPorts": [3000, 5173],
"containerEnv": {
"NODE_ENV": "development"
}
} Java / Spring Boot
{
"name": "Java Spring Boot",
"image": "mcr.microsoft.com/devcontainers/java:1-21-bullseye",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/maven:1": {
"version": "3.9"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"postCreateCommand": "./mvnw clean install -DskipTests",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"vmware.vscode-spring-boot",
"vscjava.vscode-spring-initializr"
]
}
},
"forwardPorts": [8080],
"containerEnv": {
"SPRING_PROFILES_ACTIVE": "dev"
}
} Go
{
"name": "Go",
"image": "mcr.microsoft.com/devcontainers/go:1.21-bullseye",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"postCreateCommand": "go mod download",
"customizations": {
"vscode": {
"extensions": [
"golang.go",
"golang.go-nightly"
],
"settings": {
"go.toolsManagement.autoUpdate": true,
"go.useLanguageServer": true
}
}
},
"forwardPorts": [8080],
"containerEnv": {
"CGO_ENABLED": "0"
}
} Rust
{
"name": "Rust",
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye",
"features": {
"ghcr.io/devcontainers/features/git:1": {}
},
"postCreateCommand": "cargo build",
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"vadimcn.vscode-lldb",
"serayuzgur.crates"
],
"settings": {
"rust-analyzer.checkOnSave.command": "clippy"
}
}
},
"forwardPorts": [8000]
} .NET / C#
{
"name": ".NET",
"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/azure-cli:1": {}
},
"postCreateCommand": "dotnet restore && dotnet build",
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"ms-dotnettools.vscode-dotnet-runtime",
"kreativ-software.csharpextensions"
]
}
},
"forwardPorts": [5000, 5001],
"containerEnv": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}Dev Container Features Ecosystem
Dev Container Features are shareable, versioned packages that add tools, runtimes, or configuration to any devcontainer.json with a single line. Published to OCI-compliant registries like ghcr.io, the Features specification is now mature and widely adopted across the ecosystem.
Popular Features
Build and run containers inside your dev container
Node.js runtime with nvm version management
Python with pip, venv, and Conda support
AWS CLI v2 with session manager plugin
Azure CLI with bicep and extensions
Custom Feature Authoring
Organizations can author and publish custom Features for their own internal tooling, proprietary SDKs, or standardized security configurations - distributed through private OCI registries.
// Use community and custom Features together
{
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "20"
},
"ghcr.io/devcontainers/features/aws-cli:1": {},
"ghcr.io/myorg/features/internal-sdk:2": {
"apiEndpoint": "https://api.internal.dev"
}
}
}Features use OCI artifacts for distribution, making them cacheable, versionable, and compatible with any container registry that supports OCI specs.
GPU-Enabled Dev Containers for AI/ML
Dev containers can expose host GPU hardware to containerized AI/ML workloads through the NVIDIA Container Toolkit. This enables teams to develop, train, and test machine learning models inside reproducible container environments with full CUDA acceleration.
Configuration
{
"name": "ML Workspace",
"image": "mcr.microsoft.com/devcontainers/python:3.11",
"features": {
"ghcr.io/devcontainers/features/nvidia-cuda:1": {
"cudaVersion": "12.4"
}
},
"hostRequirements": {
"gpu": "optional"
},
"runArgs": [
"--gpus=all",
"--shm-size=8g"
],
"postCreateCommand": "pip install torch torchvision"
}Key Concepts
NVIDIA Container Toolkit
Installed on the host, it exposes GPU drivers and CUDA libraries into containers. Required for any GPU passthrough.
runArgs and --gpus flag
The "runArgs": ["--gpus=all"] flag in devcontainer.json passes GPU access from the Docker daemon to the container at startup.
GPU Workspace Classes
CDE platforms like Coder and GitHub Codespaces offer GPU workspace classes (e.g., T4, A100) that pair with GPU-enabled devcontainer configs for on-demand ML environments.
Multi-Container Workspace Patterns
Real-world applications rarely run in isolation. The dockerComposeFile property in devcontainer.json lets you define multi-service workspaces where your application, database, cache, and other dependencies all run together with automatic service-to-service networking.
devcontainer.json
{
"name": "Full-Stack App",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"forwardPorts": [3000, 5432, 6379],
"portsAttributes": {
"3000": { "label": "Web App" },
"5432": { "label": "PostgreSQL" },
"6379": { "label": "Redis" }
},
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"cweijan.vscode-postgresql-client2"
]
}
}
}docker-compose.yml
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:Service Networking
Services automatically resolve each other by name. Your app connects to postgres:5432 and redis:6379 with zero configuration.
Persistent Data
Named volumes like pgdata persist database state across container rebuilds, so your test data survives environment updates.
Isolated Stacks
Each developer gets a fully isolated stack. No more shared staging databases or port conflicts between teammates.
Advanced Features
Lifecycle Scripts
{
"onCreateCommand": "git config --global ...",
"updateContentCommand": "npm update",
"postCreateCommand": "npm install",
"postStartCommand": "npm run dev",
"postAttachCommand": "echo 'Welcome!'"
}Run commands at different stages of container lifecycle
VS Code Extensions
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"settings": {
"editor.formatOnSave": true
}
}
}Auto-install extensions and configure settings
Port Forwarding
"forwardPorts": [3000, 5432],
"portsAttributes": {
"3000": {
"label": "Web App",
"onAutoForward": "notify"
},
"5432": {
"label": "PostgreSQL"
}
}Automatically forward ports with custom labels
Environment Variables
"containerEnv": {
"NODE_ENV": "development",
"DATABASE_URL": "postgres://..."
},
"remoteEnv": {
"PATH": "${containerEnv:PATH}:/custom"
}Set environment variables for container and remote sessions
Mounts & Volumes
"mounts": [
"source=/var/run/docker.sock,
target=/var/run/docker.sock,
type=bind",
"source=node_modules,
target=${containerWorkspaceFolder}/node_modules,
type=volume"
]Bind mounts for Docker socket, named volumes for dependencies
GPU Support
"hostRequirements": {
"gpu": "optional"
},
"runArgs": [
"--gpus=all"
]Enable GPU access for ML/AI workloads with CUDA support
Best Practices
Image Size Optimization
Use multi-stage builds and minimal base images. Alpine variants are smaller but may lack some tools.
# Use slim variants for smaller images
FROM node:20-slim # ~250MB instead of node:20 (~1GB)Layer Caching
Order Dockerfile commands from least to most frequently changing to maximize cache hits.
# Copy dependency files first (changes rarely)
COPY package*.json ./
RUN npm install
# Then copy source code (changes frequently)
COPY . .Security Hardening
Always run as non-root user. Pin versions to avoid supply chain attacks.
# Create and use non-root user
RUN useradd -m -s /bin/bash developer
USER developer
# Pin feature versions
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "20.11.0" }
}Version Pinning
Pin all versions - base images, features, extensions - to ensure reproducibility.
# Pin base image with digest
FROM node:20.11.0-bullseye@sha256:abc123...
# Pin feature versions
"features": {
"ghcr.io/devcontainers/features/git:1": { "version": "1.0.5" }
}Documentation
Include README.md in .devcontainer/ explaining what is pre-configured and any special setup.
.devcontainer/
|-- devcontainer.json
|-- Dockerfile
|-- docker-compose.yml
+-- README.md # Document your setup!DevContainers Across Platforms
| Platform | Support | Location | Notes |
|---|---|---|---|
VS Code (Local) | Full | Local Docker | Native support via Remote Containers extension |
GitHub Codespaces | Full | Cloud (GitHub) | Native support, reads from .devcontainer/ |
Ona (formerly Gitpod) | Partial | Cloud (Ona) | Prefers .gitpod.yml but supports devcontainer.json |
Coder | Full | Self-hosted | Via Terraform + Envbuilder agent |
DevPod | Full | Multi-cloud | Open-source client for any cloud |
JetBrains Gateway | Partial | Remote SSH | Can connect to existing containers |
Zed | Full | Local Docker | Native support added in v0.218 (January 2026) |
DevContainers for AI Agent Workspaces
DevContainers have become the standard configuration format for AI agent development environments, ensuring agents have reproducible, isolated workspaces. As AI coding assistants and autonomous agents increasingly need to build, test, and run code, DevContainers provide a safe, consistent sandbox with all required dependencies pre-configured.
Isolated Sandboxes
AI agents operate in containerized environments with defined boundaries, preventing unintended changes to host systems.
Reproducible Runs
Every agent session starts from the same known state, eliminating environment drift between automated runs.
Pre-Configured Tooling
Agents get language runtimes, build tools, and testing frameworks automatically through devcontainer.json and Features.
Publishing & Sharing DevContainers
Container Registries
Publish images to Docker Hub, GitHub Container Registry (GHCR), or private registries.
Template Repositories
Create GitHub template repos with .devcontainer/ that teams can clone and customize.
my-team-template/
|-- .devcontainer/
| |-- devcontainer.json
| +-- Dockerfile
+-- README.mdOrganization Standards
Platform teams can publish standard images and feature sets for the entire organization.
- Pre-approved tools
- Security scanning
- Compliance standards
Publishing to GitHub Container Registry
# Build and tag your image
docker build -t ghcr.io/myorg/python-dev:latest .devcontainer
# Login to GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Push the image
docker push ghcr.io/myorg/python-dev:latest
# Reference in devcontainer.json
{
"image": "ghcr.io/myorg/python-dev:latest"
}
