Dominar los contenedores de desarrollo en VS Code

Cree entornos de desarrollo consistentes, portables y reproducibles usando Dev Containers.

Índice

Los desarrolladores a menudo enfrentan el dilema “funciona en mi máquina” debido a desacuerdos en dependencias, versiones de herramientas o diferencias en el sistema operativo.
Dev Containers en Visual Studio Code (VS Code) resuelven esto elegantemente — permitiendo desarrollar dentro de un entorno contenedorizado configurado específicamente para su proyecto.

El desarrollo de software moderno requiere entornos consistentes y reproducibles que funcionen en todas las máquinas y sistemas operativos. Ya sea que estés trabajando en un proyecto de ciencia de datos en Python, una aplicación web en Node.js o un microservicio en Go, garantizar que cada miembro del equipo tenga una configuración de desarrollo idéntica puede ser un desafío.

vs code dev containers

Esta guía completa recorre qué son los Dev Containers, por qué son valiosos y cómo configurarlos en VS Code para flujos de trabajo de desarrollo portables y suaves. Aprenderás todo, desde la configuración básica hasta configuraciones avanzadas con Docker Compose y buenas prácticas para la colaboración en equipo.


🧩 ¿Qué son los Dev Containers?

Los Dev Containers son una característica proporcionada por la extensión VS Code Remote - Containers (ahora parte de VS Code Remote Development).
Permiten abrir tu proyecto en un contenedor Docker que está preconfigurado con todas tus dependencias, lenguajes y herramientas.

Píntalo así:

“Un entorno de desarrollo completamente configurado, definido como código.”

En lugar de instalar Python, Node.js, bases de datos y diversas herramientas directamente en tu máquina, las defines en archivos de configuración. Cuando abres el proyecto en VS Code, automáticamente se levanta un contenedor con todo instalado y configurado exactamente como se especificó.

Una configuración de Dev Container típicamente incluye:

  • Un Dockerfile o referencia a una imagen base (definiendo el sistema operativo, lenguajes y herramientas del contenedor)
  • Un archivo devcontainer.json (configurando ajustes del espacio de trabajo, extensiones de VS Code, reenvío de puertos, variables de entorno y comandos de inicio)
  • Opcional docker-compose.yml si tu proyecto depende de múltiples servicios (como bases de datos, Redis, colas de mensajes, etc.)

⚙️ ¿Por qué usar Dev Containers?

Aquí está lo que los hace poderosos:

  • Reproducibilidad: Cada desarrollador y sistema de CI usa exactamente el mismo entorno. No más problemas como “funciona en mi máquina pero no en la tuya”. Lo que funciona en tu laptop funcionará de forma idéntica en la máquina de tu compañero, en Windows, Mac o en una estación de trabajo Linux.

  • Aislamiento: No es necesario contaminar tu máquina local con dependencias conflictivas. Trabaja en múltiples proyectos que requieren versiones diferentes de Python, Node.js u otras herramientas sin conflictos de versiones o ajustes de entornos virtuales.

  • Portabilidad: Funciona en cualquier sistema operativo que admita Docker. Tu entorno de desarrollo viaja con tu código. Clona un repositorio, ábrelo en VS Code y estarás listo para codificar en minutos — independientemente de tu sistema operativo.

  • Consistencia en equipo: Una configuración compartida en toda tu equipe. Los nuevos miembros del equipo pueden estar operativos en minutos en lugar de pasar horas (o días) configurando su entorno de desarrollo con las herramientas y versiones correctas.

  • Automatización: Instala automáticamente extensiones de VS Code, dependencias de lenguaje y herramientas cuando abres el proyecto. Los comandos post-creación pueden ejecutar migraciones de base de datos, sembrar datos o realizar otras tareas de configuración sin intervención manual.

  • Seguridad: Aisla dependencias potencialmente riesgosas en contenedores. Si necesitas probar con una versión más antigua y vulnerable de una biblioteca, permanece contenida y no afecta tu sistema anfitrión.

Ejemplo real: Imagina unirte a un equipo que trabaja en un proyecto de microservicios que usa Python 3.11, PostgreSQL 15, Redis y Elasticsearch. Sin Dev Containers, pasarías horas instalando y configurando cada componente. Con Dev Containers, abres el proyecto en VS Code, le dejas que construya el contenedor y estarás escribiendo código dentro de 5 a 10 minutos.


🧱 Configuración de un Dev Container en VS Code

Vamos paso a paso.

1. Instalar las herramientas necesarias

Antes de comenzar, asegúrate de tener instalado lo siguiente:

  • Docker Desktop (o un entorno de contenedores equivalente como Podman)

    • Para Windows/Mac: Descarga e instala Docker Desktop
    • Para Linux: Instala Docker Engine y asegúrate de que tu usuario esté en el grupo docker
  • VS Code (recomendado la última versión)

  • La extensión Dev Containers (por Microsoft)

    • Abre VS Code
    • Ve a Extensiones (Ctrl+Shift+X o Cmd+Shift+X en macOS)
    • Busca “Dev Containers”
    • Instala la extensión con ID: ms-vscode-remote.remote-containers

Verifica tu configuración:

# Verifica que Docker esté en ejecución
docker --version
docker ps

# Debería mostrar la versión de Docker y los contenedores en ejecución (si hay alguno)

2. Inicializar el Dev Container

Abre tu carpeta de proyecto en VS Code
y abre el Panel de Comandos (Ctrl+Shift+P o Cmd+Shift+P en macOS), luego escribe y selecciona:

Dev Containers: Add Dev Container Configuration Files...

VS Code presentará una lista de plantillas de entorno predefinidas. Elige la que coincida con tu proyecto:

  • Node.js — proyectos de JavaScript/TypeScript
  • Python — ciencia de datos, aplicaciones web, scripts
  • Go — aplicaciones y servicios en Go
  • .NET — aplicaciones en C#/F#
  • Java — proyectos Spring Boot, Maven, Gradle
  • Docker-in-Docker — cuando necesitas Docker dentro de tu contenedor
  • Y muchos más…

También puedes seleccionar características adicionales como:

  • Utilidades comunes (git, curl, wget)
  • Clientes de base de datos
  • Herramientas de CLI de la nube (AWS, Azure, GCP)

Este asistente crea una carpeta .devcontainer con:

  • devcontainer.json — archivo de configuración principal
  • Dockerfile — definición de imagen personalizada (o referencia a una imagen base preconstruida)

3. Personalizar devcontainer.json

El archivo devcontainer.json es donde ocurre la magia. Aquí hay un ejemplo bien documentado para un proyecto de Node.js:

{
  // Nombre de visualización del contenedor en VS Code
  "name": "Node.js Development Container",
  
  // Configuración de construcción — puede usar Dockerfile o una imagen preconstruida
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  },
  
  // Alternativa: usar una imagen preconstruida en lugar de Dockerfile
  // "image": "mcr.microsoft.com/devcontainers/javascript-node:18",
  
  // Configuración del espacio de trabajo
  "customizations": {
    "vscode": {
      // Configuraciones de VS Code que se aplican en el contenedor
      "settings": {
        "terminal.integrated.defaultProfile.linux": "bash",
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      
      // Extensiones para instalar automáticamente
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "eamodio.gitlens",
        "ms-azuretools.vscode-docker"
      ]
    }
  },
  
  // Reenvío de puertos — hacer disponibles los puertos del contenedor en el host
  "forwardPorts": [3000, 5432],
  "portsAttributes": {
    "3000": {
      "label": "Aplicación",
      "onAutoForward": "notify"
    }
  },
  
  // Comandos para ejecutar en diferentes etapas
  "postCreateCommand": "npm install",     // Después de crear el contenedor
  "postStartCommand": "npm run dev",      // Después de iniciar el contenedor
  
  // Variables de entorno
  "containerEnv": {
    "NODE_ENV": "development",
    "PORT": "3000"
  },
  
  // Ejecutar el contenedor como usuario no root (recomendado para seguridad)
  "remoteUser": "node",
  
  // Montar volúmenes adicionales
  "mounts": [
    "source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,readonly,type=bind"
  ]
}

Explicación de opciones clave:

  • name — nombre de visualización mostrado en la barra de estado de VS Code
  • build / image — usar un Dockerfile o una imagen preconstruida
  • customizations.vscode.extensions — extensiones de VS Code para instalar automáticamente
  • forwardPorts — puertos para exponer del contenedor al host
  • postCreateCommand — se ejecuta una vez cuando se crea el contenedor (ej. instalar dependencias)
  • postStartCommand — se ejecuta cada vez que se inicia el contenedor
  • containerEnv — variables de entorno disponibles en el contenedor
  • remoteUser — cuenta de usuario para usar dentro del contenedor
  • mounts — archivos o carpetas adicionales para montar (como claves SSH)

💡 Consejos profesionales:

  • Usa postCreateCommand para operaciones lentas (npm install, pip install)
  • Usa postStartCommand para tareas de inicio rápido (migraciones de base de datos)
  • Siempre especifique las extensiones que necesita tu proyecto — esto asegura una herramienta consistente
  • Usa variables de entorno para configuraciones que varían entre desarrolladores

4. Construir y abrir en el contenedor

Una vez que tu configuración esté lista, es hora de lanzar tu entorno de desarrollo:

Abrir el Panel de Comandos (Ctrl+Shift+P / Cmd+Shift+P) y ejecutar:

Dev Containers: Reopen in Container

¿Qué sucede a continuación:

  1. Construcción de la imagen — VS Code construye la imagen Docker basada en tu Dockerfile o extrae una imagen preconstruida. Esto puede tomar unos minutos la primera vez.

  2. Creación del contenedor — Docker crea un nuevo contenedor a partir de la imagen construida.

  3. Montaje de volúmenes — Tu carpeta de proyecto se monta en el contenedor, haciendo accesible tu código dentro de él.

  4. Instalación de extensiones — Todas las extensiones de VS Code especificadas se instalan automáticamente en el contenedor.

  5. Comandos post-creación — Tu postCreateCommand se ejecuta (ej. npm install, pip install -r requirements.txt).

  6. ¡Listo! — VS Code se reconecta al contenedor y ahora estás desarrollando dentro de él.

Verifica que estés en el contenedor:

Puedes confirmar que estás trabajando dentro del contenedor abriendo un terminal y ejecutando:

# Verificar el sistema operativo
uname -a
# Salida: Linux ... (kernel del contenedor)

# Verificar el nombre del host (normalmente el ID del contenedor)
hostname
# Salida: abc123def456

# Verificar procesos en ejecución
ps aux
# Verás procesos del contenedor, no de tu sistema anfitrión

Nota que la barra de estado de VS Code (inferior izquierda) ahora muestra: Dev Container: [Nombre de tu contenedor]

Comandos del ciclo de vida del contenedor:

  • Reconstruir el contenedorDev Containers: Rebuild Container (cuando cambias el Dockerfile)
  • Reconstruir sin cachéDev Containers: Rebuild Container Without Cache (para una construcción fresca)
  • Reabrir localmenteDev Containers: Reopen Folder Locally (salir del contenedor, trabajar en el host)

5. Añadir servicios adicionales (opcional)

Las aplicaciones reales suelen depender de bases de datos, capas de caché, colas de mensajes u otros servicios. Puedes usar Docker Compose para orquestar múltiples contenedores.

Ejemplo: Aplicación full-stack con Node.js, PostgreSQL y Redis

Crea un docker-compose.yml en tu carpeta .devcontainer:

version: "3.8"

services:
  # Contenedor de desarrollo principal
  app:
    build: 
      context: ..
      dockerfile: .devcontainer/Dockerfile
    
    volumes:
      # Montar carpeta del proyecto
      - ..:/workspace:cached
      # Usar volumen con nombre para node_modules (mejor rendimiento)
      - node_modules:/workspace/node_modules
    
    # Mantener el contenedor en ejecución
    command: sleep infinity
    
    # Acceso de red a otros servicios
    depends_on:
      - db
      - redis
    
    environment:
      DATABASE_URL: postgresql://dev:secret@db:5432/appdb
      REDIS_URL: redis://redis:6379

  # Base de datos PostgreSQL
  db:
    image: postgres:15-alpine
    restart: unless-stopped
    volumes:
      - postgres-data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"

  # Cache Redis
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"

volumes:
  postgres-data:
  redis-data:
  node_modules:

Luego, actualiza tu devcontainer.json para usar Docker Compose:

{
  "name": "Entorno de desarrollo full-stack",
  
  // Usar docker-compose en lugar de un solo contenedor
  "dockerComposeFile": "docker-compose.yml",
  
  // ¿Qué servicio usar como contenedor de desarrollo?
  "service": "app",
  
  // Ruta a la carpeta de trabajo dentro del contenedor
  "workspaceFolder": "/workspace",
  
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-azuretools.vscode-docker",
        "ckolkman.vscode-postgres"  // Cliente de PostgreSQL
      ]
    }
  },
  
  "forwardPorts": [3000, 5432, 6379],
  
  "postCreateCommand": "npm install && npm run db:migrate",
  
  "remoteUser": "node"
}

¿Qué ofrece esta configuración:

  • app — Tu contenedor de desarrollo con Node.js
  • db — Base de datos PostgreSQL, accesible en db:5432 desde tu aplicación
  • redis — Cache Redis, accesible en redis:6379
  • Volúmenes con nombre — Persistir datos de la base de datos entre reinicios del contenedor
  • Reenvío de puertos — Acceder a todos los servicios desde tu máquina anfitriona

Conectar a servicios desde tu código:

// En tu aplicación de Node.js
const { Pool } = require('pg');
const redis = require('redis');

// Conexión a PostgreSQL
const pool = new Pool({
  connectionString: process.env.DATABASE_URL
  // Resuelve a: postgresql://dev:secret@db:5432/appdb
});

// Conexión a Redis
const redisClient = redis.createClient({
  url: process.env.REDIS_URL
  // Resuelve a: redis://redis:6379
});

Acceder a servicios desde tu máquina anfitriona:

  • App: http://localhost:3000
  • PostgreSQL: localhost:5432 (usando cualquier cliente de PostgreSQL)
  • Redis: localhost:6379 (usando redis-cli o herramientas gráficas)

Ahora, cuando abras el proyecto en VS Code, todos los servicios se iniciarán automáticamente!


🧠 Consejos avanzados y buenas prácticas

Usar imágenes preconstruidas

Ahorra tiempo de construcción significativo empezando desde las imágenes oficiales de devcontainer de Microsoft:

{
  "image": "mcr.microsoft.com/devcontainers/python:3.11",
  "features": {
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {}
  }
}

Features son scripts de instalación reutilizables para herramientas comunes (Git, GitHub CLI, Node, AWS CLI, etc.).

Buenas prácticas de control de versiones

Siempre comita tu carpeta .devcontainer:

git add .devcontainer/
git commit -m "Añadir configuración de Dev Container"
git push

Esto asegura:

  • ✅ Nuevos miembros del equipo obtienen el entorno automáticamente
  • ✅ Los cambios del entorno se rastrean y revisan
  • ✅ Todos desarrollan en la misma configuración

Consejo profesional: Añade una sección README que explique la configuración del contenedor de desarrollo:

## Configuración de desarrollo

Este proyecto usa Dev Containers de VS Code. Para comenzar:

1. Instalar Docker Desktop y VS Code
2. Instalar la extensión "Dev Containers"
3. Clonar este repositorio
4. Abrir en VS Code
5. Hacer clic en "Reopen in Container" cuando se le pida

Depuración en contenedores

La depuración funciona de forma inmediata. Configura tu launch.json como de costumbre:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Node.js",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/index.js",
      "skipFiles": ["<node_internals>/**"]
    }
  ]
}

Establece puntos de interrupción y depura normalmente — VS Code maneja automáticamente la conexión al contenedor.

Paridad con Continuous Integration

Usa la misma imagen de contenedor en tu pipeline de CI/CD:

# Ejemplo de GitHub Actions
name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/devcontainers/javascript-node:18
    steps:
      - uses: actions/checkout@v3
      - run: npm install
      - run: npm test

Esto asegura paridad entre desarrollo y producción — si las pruebas pasan localmente, pasarán en CI.

Optimización de rendimiento

Para usuarios de macOS/Windows — usa volúmenes con nombre para dependencias:

{
  "mounts": [
    "source=myproject-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume"
  ]
}

Esto mejora significativamente el rendimiento de E/S para node_modules, venv, etc.

Desarrollo multi-estadio

Crea configuraciones diferentes para diferentes roles de equipo:

.devcontainer/
├── devcontainer.json          # Default (full-stack)
├── frontend/
│   └── devcontainer.json      # Solo frontend (más ligero)
└── backend/
    └── devcontainer.json      # Solo backend (con DB)

Los miembros del equipo pueden elegir su entorno cuando abran el proyecto.

Trabajar con claves SSH y Git

Monta tus claves SSH para operaciones de Git:

{
  "mounts": [
    "source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/node/.ssh,readonly,type=bind"
  ],
  "postCreateCommand": "ssh-add ~/.ssh/id_ed25519 || true"
}

Archivos de entorno personalizados

Carga configuración específica del entorno:

{
  "runArgs": ["--env-file", ".devcontainer/.env"]
}

.devcontainer/.env:

API_KEY=dev_key_here
DEBUG=true
LOG_LEVEL=debug

🔧 Problemas comunes de solución

El contenedor no inicia

Error: No se puede conectar al demonio de Docker

Solución:

  • Asegúrate de que Docker Desktop esté en ejecución
  • En Linux, verifica: sudo systemctl status docker
  • Verifica que Docker esté en tu PATH: docker --version

Rendimiento lento en macOS/Windows

Problema: Las operaciones de archivos son lentas

Soluciones:

  1. Usa volúmenes con nombre para node_modules, venv, etc.

  2. Habilita el compartir archivos en la configuración de Docker Desktop

  3. Considera usar opciones de montaje cached o delegated:

    "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached"
    

Extensiones no se instalan

Problema: Las extensiones especificadas en devcontainer.json no se instalan

Soluciones:

  1. Reconstruye el contenedor: Dev Containers: Rebuild Container
  2. Verifica que los IDs de las extensiones sean correctos
  3. Asegúrate de que las extensiones admitan contenedores remotos (la mayoría lo hacen)

Puerto ya en uso

Error: El puerto 3000 ya está asignado

Soluciones:

  1. Detén los contenedores conflictivos: docker ps y docker stop <container>
  2. Cambia el mapeo de puertos en forwardPorts
  3. Usa puertos dinámicos: VS Code asignará automáticamente puertos disponibles

Cambios en Dockerfile no aplicados

Problema: Modificaste el Dockerfile pero los cambios no se aplican

Solución: Reconstruye sin caché:

Dev Containers: Rebuild Container Without Cache

El contenedor se cierra inmediatamente

Problema: El contenedor inicia y luego se cierra

Solución: Añade un comando para mantenerlo en ejecución en docker-compose.yml:

command: sleep infinity

O en devcontainer.json:

{
  "overrideCommand": true
}

✅ Conclusión

Los Dev Containers en VS Code traen consistencia, simplicidad y automatización a tu flujo de trabajo de desarrollo. Transforman configuraciones complejas y frágiles en entornos definidos por código que simplemente funcionan, independientemente de tu máquina o sistema operativo.

Conclusión clave:

  • 🎯 Eliminar problemas de “funciona en mi máquina” — Todos usan entornos idénticos
  • 🚀 Mayor velocidad de onboarding — Nuevos miembros del equipo productivos en minutos, no en días
  • 🔒 Mejor seguridad — Aislar dependencias del sistema anfitrión
  • 📦 Portabilidad — Tu entorno viaja con tu código
  • 🤝 Consistencia en equipo — No más conflictos de versiones de dependencias
  • 🔄 Paridad con CI/CD — Usa la misma imagen en desarrollo y en integración continua

Ya sea que estés trabajando en un simple script de Python o en una arquitectura compleja de microservicios con múltiples bases de datos, los Dev Containers ofrecen una base sólida para el desarrollo moderno.

Si colaboras en proyectos multilenguaje, contribuyes a repositorios de código abierto, onboards nuevos desarrolladores con frecuencia o simplemente deseas entornos de desarrollo limpios y reproducibles — los Dev Containers son una herramienta esencial en tu pila.

Empieza pequeño: prueba Dev Containers en tu próximo proyecto. Una vez que experimentes los beneficios, te preguntarás cómo desarrollaste sin ellos.


📚 Recursos útiles y artículos relacionados

Documentación oficial:

Artículos relacionados en este sitio: