Dev Container — un entorno de desarrollo unificado para Mac / Windows / Linux
Usa VS Code Dev Containers para eliminar las diferencias entre sistemas operativos. Un entorno de desarrollo reproducible basado en Docker, definido por un único devcontainer.json que comparte todo el equipo.
«Funciona en mi máquina» casi siempre tiene su origen en diferencias entre sistemas operativos. Mac tiene brew y Apple Silicon, Windows tiene PowerShell y UTF-16, Linux tiene systemd. Aunque todos instalen la misma versión de Node y los mismos paquetes, siempre aparecen discrepancias sutiles.
Los Dev Containers (VS Code Dev Containers + el estándar devcontainer.json) encapsulan las diferencias de SO dentro de un contenedor Docker. Todo el equipo trabaja en el mismo entorno Linux; solo el IDE se ejecuta en el host. Esta guía configura un único dev container que funciona de forma idéntica en hosts Mac, Windows y Linux.
TL;DR
- Instala un runtime de Docker (Mac: OrbStack/Docker Desktop, Win: Docker Desktop/WSL Docker, Linux: docker)
- VS Code + la extensión Dev Containers (
ms-vscode-remote.remote-containers) - Coloca un
.devcontainer/devcontainer.jsonen la raíz del proyecto Cmd/Ctrl+Shift+P→ Dev Containers: Reopen in Container — el IDE se reinicia dentro del contenedor- Un compañero de equipo: clona → ejecuta ese comando una vez → entorno idéntico
Requisitos previos
- VS Code 1.95+
- Runtime de Docker — guías por SO: mac/docker-setup · windows/docker-wsl2
- Proyecto bajo Git (para compartir devcontainer.json)
1. Por qué usar Dev Containers
Problemas que resuelve
- «Funciona en mi máquina» — dentro del contenedor, el SO y la cadena de herramientas son idénticos
- Incorporación rápida — los nuevos compañeros no leen un README de 30 líneas; hacen clic una vez
- Conflictos de versión — Proyecto A en Node 18, B en Node 22, aislados sin mise/nvm en el host
- Problemas de compilación específicos del SO — evita los problemas ARM de Apple Silicon, las rutas largas de Windows, etc.
- Paridad con CI — usa la misma imagen base que tu flujo de trabajo de GitHub Actions y la brecha desaparece
Puntos débiles de Dev Containers
- Desarrollo de apps con GUI — las compilaciones nativas de Mac o Windows no encajan en un contenedor
- Cargas de trabajo con GPU — el paso de GPU es posible, pero ejecutar en el host resulta más estable
- Máquinas de bajo rendimiento — Docker consume 1–2 GB adicionales de RAM (aceptable con 16 GB)
2. devcontainer.json mínimo
.devcontainer/devcontainer.json:
{
"name": "My Project Dev",
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postCreateCommand": "npm install",
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
},
"forwardPorts": [3000, 5173],
"remoteUser": "node"
}Campos clave:
| Campo | Función |
|---|---|
image | Imagen base de Docker (Node / Python / Go / Rust, etc.) |
features | Herramientas adicionales (git, gh, docker-in-docker, awscli — seleccionables desde un catálogo) |
postCreateCommand | Se ejecuta una vez tras crear el contenedor (instalar dependencias, etc.) |
customizations.vscode.extensions | Extensiones de VS Code que se instalan automáticamente dentro del contenedor |
forwardPorts | Puertos expuestos al host |
remoteUser | Usuario dentro del contenedor (se recomienda no usar root por seguridad) |
3. Primera ejecución
Paleta de comandos de VS Code (Cmd/Ctrl+Shift+P) → Dev Containers: Reopen in Container.
- Primera vez: ~1–3 minutos (descarga de imagen +
postCreateCommand) - La esquina inferior izquierda de VS Code muestra
Dev Container: My Project Dev
# Terminal dentro del contenedor (Terminal de VS Code)
uname -a
# Linux 1234abcd 6.10.x #1 SMP Debian ...
which node
# /usr/local/share/nvm/versions/node/v22.x/bin/nodeEl mismo entorno Linux, independientemente del SO del host.
4. Elegir una imagen base
El campo image: en devcontainer.json. Catálogo oficial:
| Lenguaje / stack | Imagen |
|---|---|
| Node.js | mcr.microsoft.com/devcontainers/javascript-node:1-{18,20,22}-bookworm |
| Python | mcr.microsoft.com/devcontainers/python:1-{3.11,3.12,3.13}-bookworm |
| Go | mcr.microsoft.com/devcontainers/go:1-{1.22,1.23} |
| Rust | mcr.microsoft.com/devcontainers/rust:1-bookworm |
| Java | mcr.microsoft.com/devcontainers/java:1-{17,21}-bookworm |
| .NET | mcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm |
| Universal (políglota) | mcr.microsoft.com/devcontainers/universal:2-linux |
Recomendación: proyecto de un único lenguaje → imagen de ese lenguaje. Proyecto políglota → imagen base + features.
O escribe tu propio Dockerfile:
{
"name": "My Custom Dev",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
}
}5. features — añade una herramienta con una línea
El bloque features usa módulos del catálogo oficial (https://containers.dev/features). Con una sola línea, la herramienta queda instalada.
Opciones habituales:
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}, // ejecuta docker dentro del contenedor
"ghcr.io/devcontainers/features/aws-cli:1": {},
"ghcr.io/devcontainers/features/terraform:1": {},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
}6. Docker-in-Docker — contenedores dentro del contenedor
Cuando necesitas ejecutar docker run dentro del dev container (pruebas con docker-compose, verificación de compilaciones):
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"dockerDashComposeVersion": "v2"
}
}O comparte el socket de Docker del host (más ligero, pero con menor aislamiento de seguridad):
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
]7. Montajes de archivos host ↔ contenedor
La carpeta de trabajo se monta automáticamente en /workspaces/{proyecto}.
Montajes adicionales:
"mounts": [
"source=${localEnv:HOME}/.aws,target=/home/node/.aws,type=bind,readonly",
"source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind"
]Para el rendimiento de montajes en macOS, consulta docker-setup §5.2 (se recomienda VirtioFS).
8. Variables de entorno y secretos
Variables de entorno estáticas
"containerEnv": {
"NODE_ENV": "development",
"LOG_LEVEL": "debug"
}Secretos (nunca en el repositorio)
Monta un archivo .env o pásalo mediante localEnv:
"containerEnv": {
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}",
"OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}"
}Las variables de entorno del shell del host se inyectan en el contenedor. Asegúrate de que .env esté en .gitignore.
9. Multi-contenedor (integración con docker-compose)
Para dependencias como base de datos o Redis:
.devcontainer/docker-compose.yml:
services:
app:
image: mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm
volumes:
- ..:/workspaces/myapp
command: sleep infinity
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: dev
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:.devcontainer/devcontainer.json:
{
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/myapp"
}VS Code se conecta al contenedor app; db arranca automáticamente.
10. Mismo entorno que CI — devcontainer-cli
Reutiliza devcontainer.json en CI:
npm install -g @devcontainers/cli
# Compila el contenedor y ejecuta un comando
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . npm testGitHub Actions:
# .github/workflows/test.yml
- name: Test in dev container
uses: devcontainers/ci@v0.3
with:
runCmd: npm testCI en verde = mismo resultado que en local.
11. Verificación
# Host
docker --version
# Dentro del contenedor (Terminal de VS Code)
uname -a # Linux ...
which node # /usr/local/share/...
echo $NODE_ENV # development (containerEnv aplicado)
node --version # la versión de Node del contenedor
git --version # instalada mediante featuresLas cinco respuestas correctas dentro del contenedor = configuración completada.
12. Resolución de problemas
«Failed to connect» / el contenedor no arranca
- ¿Está el runtime de Docker activo? (barra de menú de Docker Desktop,
orb status,colima status) docker pspara detectar colisiones de puertos o nombres- VS Code → Paleta de comandos → «Dev Containers: Rebuild Without Cache»
Las compilaciones tardan demasiado
- La primera compilación descarga la imagen base (varios GB) — es lo esperado
- Las siguientes usan la caché.
postCreateCommandno se cachea (se ejecuta en cada compilación) - Distingue
onCreateCommand(solo la primera vez) depostCreateCommand(en cada nueva compilación)
Permiso denegado en archivos
- El UID del contenedor y el del host no coinciden (habitual en hosts Linux)
- Establece
remoteUserjunto conupdateRemoteUserUID:"remoteUser": "vscode", "updateRemoteUserUID": true
forwardPorts no funciona
- ¿La app dentro del contenedor escucha en
0.0.0.0o::(no en127.0.0.1)? - Ejemplo: Next.js necesita
next dev -H 0.0.0.0
Git config del host no se detecta
- El montaje automático de
.gitconfiges opcional; añádelo explícitamente enmounts - O bien usa el git instalado mediante
features, que copia elgitconfigdel host
El IDE es lento con node_modules muy grandes
- Excluye
node_modulesdel watcher de VS Code (files.watcherExclude) - O coloca
node_modulesen un volumen propio del contenedor, en lugar de un montaje del host:"mounts": [ "source=${localWorkspaceFolderBasename}-node-modules,target=/workspaces/${localWorkspaceFolderBasename}/node_modules,type=volume" ]
13. Siguientes pasos
- Mac Docker — /mac/docker-setup (comparativa de runtimes)
- Windows Docker WSL2 — /windows/docker-wsl2
- Desarrollo remoto — /multi-os/remote-development — usa una máquina remota como entorno de desarrollo
- Sincronización de dotfiles — /mac/dotfiles — aplica dotfiles dentro del dev container mediante chezmoi
Referencias
- Especificación Dev Containers
- Documentación VS Code Dev Containers
- Catálogo de features
- devcontainer-cli
Historial de cambios
- 2026-05-16: Primer borrador. Fundamentos de devcontainer.json · selección de imagen base · features · integración con docker-compose · integración con CI · seis casos de resolución de problemas.