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.
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",
// VS Code customization
"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/$USERNAME
Docker 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-alpine
Features System
Features are self-contained, reusable units of installation code. Think of them as modular add-ons:
devcontainers/features/git
devcontainers/features/node
devcontainers/features/python
devcontainers/features/docker-in-docker
devcontainers/features/kubectl-helm
devcontainers/features/aws-cli
Common 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"
}
}
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/ |
|
Gitpod
|
Partial | Cloud (Gitpod) | 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 |
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.md
Organization 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"
}