Dev Containers: Reproducible Development Environments
In this tutorial, you'll learn dev containers including VS Code Dev Containers, devcontainer.json configuration, container features, and creating consistent development environments across your team.
Why Dev Containers Matter
Onboarding a new developer should take minutes, not days. Dev containers package the entire development environment -- language runtime, tools, extensions, and configuration -- into a portable container definition stored in your Repository. When a developer opens the project in VS Code, Dev Containers automatically builds and attaches to the container. No manual setup, no configuration drift.
By the end of this guide, you will create dev container configurations, customize them for different languages, and share them with your team.
What are Dev Containers?
Dev Containers are Docker containers configured as full-featured development environments. The configuration lives in a .devcontainer directory at the project root. VS Code's Dev Containers extension builds the container and connects to it, providing a seamless IDE experience inside the container.
flowchart LR A[Project Repo] --> B[.devcontainer/] B --> C[devcontainer.json] B --> D[Dockerfile] C --> E[Extensions] C --> F[Settings] C --> G[Ports] C --> H[Post-Create Commands] D --> I[Base Image] I --> J[OS + Runtime + Tools]
Basic Setup
Install the Dev Containers extension in VS Code:
Name: Dev Containers
Id: ms-vscode-remote.remote-containers
Project Structure
my-project/
├── .devcontainer/
│ ├── devcontainer.json
│ └── Dockerfile
├── src/
├── package.json
└── README.md
devcontainer.json
{
"name": "Node.js Development",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-vscode.vscode-typescript-next",
"github.copilot]
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
},
"forwardPorts": [3000, 5173],
"postCreateCommand": "npm install",
"remoteUser": "node"
}
Dockerfile
FROM node:20-slim
RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g npm@latest
WORKDIR /workspace
Opening a Project in a Dev Container
- Clone the Repository
- Open in VS Code
- Run
Dev Containers: Reopen in Containerfrom the command palette - VS Code builds the container (first time) and reopens the window
Expected Behavior
$ [Dev Containers] Starting Dev Container...
$ [Dev Containers] Building Docker image from Dockerfile...
$ [Dev Containers] Running postCreateCommand...
$ [Dev Containers] Container ready!
After the container starts, VS Code shows "Dev Container: Node.js Development" in the bottom-left status bar.
Language-Specific Configurations
Python Dev Container
{
"name": "Python 3 Development",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"charliermarsh.ruff]
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false
}
}
},
"forwardPorts": [8000],
"postCreateCommand": "pip install --user -r requirements.txt",
"remoteUser": "vscode"
}
FROM mcr.microsoft.com/devcontainers/python:3.12
RUN pip install --upgrade pip
EXPOSE 8000
Rust Dev Container
{
"name": "Rust Development",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"vadimcn.vscode-lldb",
"tamasfe.even-better-toml",
"serayuzgur.crates]
],
"settings": {
"rust-analyzer.checkOnSave.command": "clippy"
}
}
},
"features": {
"ghcr.io/devcontainers/features/rust:1": {}
},
"postCreateCommand": "cargo build",
"forwardPorts": [8080]
}
Features
Dev Container Features are reusable, self-contained units that add tools and runtimes to a container.
{
"name": "Full Stack Development",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"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:1": {},
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
}
}
Available Features
| Feature | Description |
|---|---|
node |
Node.js and npm |
python |
Python and pip |
java |
JDK and Maven/Gradle |
go |
Go compiler |
rust |
Rust toolchain |
docker-in-docker |
Docker inside the container |
<a href="/devops/terraform/">Terraform</a> |
Terraform CLI |
aws-cli |
AWS command-line tools |
Environment Variables and Secrets
{
"name": "Development with Secrets",
"build": {
"dockerfile": "Dockerfile"
},
"containerEnv": {
"NODE_ENV": "development",
"DATABASE_HOST": "localhost"
},
"remoteEnv": {
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}"
}
}
Use .env files for secrets:
{
"runArgs": ["--env-file", ".devcontainer/.env"]
}
Advanced Configuration
Multiple Container Setup
{
"name": "Full Stack with Database",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"forwardPorts": [3000, 8080, 5432]
}
# .devcontainer/docker-compose.yml
version: '3.9'
services:
app:
build: .
volumes:
- ..:/workspace:cached
command: sleep infinity
environment:
DATABASE_URL: postgresql://user:pass@db:5432/myapp
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
Lifecycle Hooks
{
"onCreateCommand": "echo 'Container created'",
"updateContentCommand": "npm install",
"postCreateCommand": "npm run build",
"postStartCommand": "npm run dev &",
"postAttachCommand": "echo 'Attached!'"
}
Sharing with Your Team
Store the .devcontainer directory in your Repository. When team members open the project, VS Code prompts them to reopen in the container.
git add .devcontainer/
git commit -m "Add dev container configuration"
git push
Common Errors
| Problem | Cause | Fix |
|---|---|---|
Docker is not running |
Docker not started | Start Docker Desktop or Docker daemon |
Failed to connect to bus |
Systemd not available | Not needed -- dev containers don't use systemd |
Permission denied when writing files |
UID mismatch | Use remoteUser to match your host UID |
Extension not found |
Extension ID misspelled | Verify the extension ID from the marketplace |
postCreateCommand failed |
Command error in container | Check the logs in VS Code's Dev Containers output panel |
Practice Questions
1. Where does the dev container configuration live in a project?
In a .devcontainer/ directory at the project root.
2. What is the purpose of postCreateCommand?
It runs a command after the container is created, typically for installing dependencies.
3. How do you use Docker Compose with dev containers?
Set dockerComposeFile and service in devcontainer.json to reference a Docker Compose configuration.
4. What are Dev Container Features?
Reusable units that add tools and runtimes to a container (e.g., Node.js, Python, Docker-in-Docker).
5. How do you forward ports from a dev container to your host machine?
Use the forwardPorts array in devcontainer.json.
Challenge
Create a dev container configuration for a full-stack project using Node.js with React frontend and Python backend. Include: both runtimes, database client tools, VS Code extensions for both languages, port forwarding for frontend and backend, and a post-create command that installs all dependencies.
Real-World Task
Take an existing project and create a .devcontainer/devcontainer.json configuration. Install the Dev Containers extension, reopen the project in the container, and verify that all development workflows work: running tests, starting the dev server, debugging with breakpoints, and using the integrated terminal. Share the configuration with your team by committing it to the Repository.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro