Tips & Tricks (Actualizado: 2/6/2026)

Claude Code y Devcontainer: entorno de desarrollo reproducible

Crea un devcontainer Claude Code reproducible con Dockerfile, permisos, secretos, volumes y port-forward.

Claude Code y Devcontainer: entorno de desarrollo reproducible

Claude Code funciona mejor cuando el entorno de desarrollo deja de ser una sorpresa. Si una persona usa Node.js 22, otra conserva Node.js 20, PostgreSQL está instalado de forma distinta en cada portátil y Redis solo existe en algunas máquinas, el agente acaba resolviendo problemas de entorno antes que problemas de producto.

Un Dev Container convierte esa configuración en parte del repositorio. El editor se conecta a un contenedor Docker y allí ejecuta la terminal, el servidor de lenguaje, los tests, las herramientas de base de datos y Claude Code. En esta guía construiremos un devcontainer práctico para Next.js y TypeScript, con PostgreSQL, Redis, Dockerfile, postCreateCommand, permisos, secretos, volumes y port-forward.

El 2 de junio de 2026, la documentación oficial de Claude Code incluye una página de Development containers con notas sobre la Feature oficial, persistencia de credenciales, restricciones de red y riesgos de saltarse permisos. Para el estándar de contenedores, conviene tener a mano la documentación de VS Code Dev Containers y la Dev Container Specification.

Por qué un devcontainer encaja con Claude Code

Claude Code no solo sugiere cambios. Puede leer archivos, editar código, ejecutar comandos, revisar fallos de tests y proponer el siguiente paso. Por eso importa mucho dónde se ejecuta. Si trabaja en la shell del host, puede ver paquetes globales antiguos, credenciales personales, variables de otros proyectos o servicios locales que el resto del equipo no tiene.

Con un devcontainer, la superficie de trabajo queda definida en código. La versión de Node.js, los paquetes del sistema, la versión del CLI de Claude Code, las extensiones de VS Code, los scripts de ciclo de vida, los volumes y los puertos reenviados se pueden revisar en el mismo pull request. Cuando Claude Code diga que ejecutó npm test, el equipo sabrá en qué entorno lo hizo.

flowchart LR
  Host["Equipo local"] --> Editor["VS Code / Cursor"]
  Editor --> Container["Dev Container"]
  Container --> Claude["Claude Code CLI"]
  Container --> Tools["Node.js / npm / psql / redis-cli"]
  Container --> Services["PostgreSQL / Redis"]
  Container --> Repo["Repositorio montado"]
  Claude --> Repo
  Claude --> Tools

Hay tres casos de uso claros. El primero es onboarding: una persona nueva reconstruye el contenedor y obtiene la misma base que el resto del equipo. El segundo es desarrollo remoto: la misma carpeta .devcontainer sirve en local y en Codespaces. El tercero es depuración asistida por IA: lint, typecheck y tests dejan de depender de una máquina concreta.

Archivos que vamos a crear

La configuración separa aplicación, servicios y estado de Claude Code. Esa separación evita montar el directorio home del host por comodidad.

ArchivoFunciónQué revisar
.devcontainer/devcontainer.jsonPunto de entrada para el editorremoteUser, puertos, mounts, comandos de ciclo de vida
.devcontainer/DockerfileInstala herramientas y Claude Codeversión del CLI, usuario no root, paquetes del sistema
.devcontainer/docker-compose.ymlLevanta app, PostgreSQL y Redisvolumes, health checks, puertos publicados
.devcontainer/post-create.shSetup después de crear el contenedorlockfiles, Prisma, errores visibles
.claude/settings.jsonReglas de permisos para Claude Code.env, secretos, push y comandos destructivos

devcontainer.json listo para copiar

Crea .devcontainer/devcontainer.json. Es JSON real, así que no incluyas comentarios dentro del archivo.

{
  "name": "claude-code-next-dev",
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspaces/app",
  "remoteUser": "node",
  "shutdownAction": "stopCompose",
  "waitFor": "postCreateCommand",
  "postCreateCommand": "bash .devcontainer/post-create.sh",
  "postStartCommand": "git config --global --add safe.directory /workspaces/app || true",
  "forwardPorts": [3000, 5432, 6379],
  "portsAttributes": {
    "3000": { "label": "Next.js", "onAutoForward": "notify" },
    "5432": { "label": "PostgreSQL", "onAutoForward": "silent" },
    "6379": { "label": "Redis", "onAutoForward": "silent" }
  },
  "mounts": [
    "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
  ],
  "containerEnv": {
    "NODE_ENV": "development",
    "DISABLE_AUTOUPDATER": "1",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "anthropic.claude-code",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-azuretools.vscode-docker"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "typescript.tsdk": "node_modules/typescript/lib",
        "terminal.integrated.defaultProfile.linux": "bash"
      }
    }
  }
}

El valor de remoteUser debe ser un usuario no root. El contenedor reduce el riesgo, pero el workspace sigue montado desde el host. Si Claude Code borra un archivo en /workspaces/app, también desaparece del repositorio local. El volume para /home/node/.claude conserva la configuración de Claude Code entre rebuilds sin exponer todo el home del host.

Docker Compose, Dockerfile y setup inicial

Este docker-compose.yml levanta la app, PostgreSQL y Redis. La app se queda en sleep infinity para que el editor pueda adjuntarse al contenedor.

services:
  app:
    build:
      context: ..
      dockerfile: .devcontainer/Dockerfile
    command: sleep infinity
    volumes:
      - ..:/workspaces/app:cached
      - node_modules:/workspaces/app/node_modules
      - claude_code_config:/home/node/.claude
    environment:
      DATABASE_URL: postgresql://app:app_password@db:5432/app
      REDIS_URL: redis://redis:6379
      NEXT_TELEMETRY_DISABLED: "1"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app_password
      POSTGRES_DB: app
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 5s
      retries: 20

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 20

volumes:
  node_modules:
  claude_code_config:
  postgres_data:
  redis_data:

En el Dockerfile fijamos la versión de Claude Code. Para esta guía comprobé el paquete el 2 de junio de 2026 y npm view @anthropic-ai/claude-code version devolvió 2.1.160.

FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm

ARG CLAUDE_CODE_VERSION=2.1.160

ENV DISABLE_AUTOUPDATER=1
ENV NEXT_TELEMETRY_DISABLED=1

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
      jq \
      postgresql-client \
      redis-tools \
      ripgrep \
    && rm -rf /var/lib/apt/lists/*

RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
    && npm cache clean --force

USER node
WORKDIR /workspaces/app

RUN mkdir -p /home/node/.claude

El postCreateCommand llama a un script para respetar el gestor de paquetes del proyecto.

#!/usr/bin/env bash
set -euo pipefail

cd /workspaces/app
corepack enable

if [ -f pnpm-lock.yaml ]; then
  pnpm install --frozen-lockfile
elif [ -f yarn.lock ]; then
  yarn install --immutable
elif [ -f package-lock.json ]; then
  npm ci
elif [ -f package.json ]; then
  npm install
fi

if [ -f prisma/schema.prisma ]; then
  npx prisma generate
fi

node --version
npm --version
claude --version || true

La clave es que el setup falle cuando algo está mal. Si npm ci o Prisma fallan y el comando continúa, Claude Code puede empezar a modificar una base rota. También evita iniciar npm run dev dentro de postCreateCommand; un servidor permanente debe ejecutarse desde una terminal o task.

Permisos, secretos, volumes y port-forward

No montes ~/.ssh, ~/.aws, ~/.config/gcloud ni .env de producción solo porque el contenedor lo permite. El secreto más seguro es el que no entra en el contenedor. Para desarrollo, usa valores internos como postgresql://app:app_password@db:5432/app; para producción, usa secrets gestionados o tokens temporales.

Las reglas de Claude Code pueden vivir en .claude/settings.json.

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run test *)",
      "Bash(npm run typecheck)",
      "Bash(claude --version)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(printenv *)",
      "Bash(git push *)",
      "Bash(docker system prune *)"
    ]
  }
}

Los volumes también necesitan criterio. node_modules debe vivir dentro del contenedor para evitar mezclar módulos nativos de Windows o macOS con Linux. El volume de PostgreSQL es útil, pero puede conservar un estado viejo después de varios experimentos de migración. Documenta cuándo se puede ejecutar docker compose -f .devcontainer/docker-compose.yml down -v.

Para puertos, empieza con forwardPorts. Publicar 5432 o 6379 con Compose ports solo tiene sentido si una herramienta externa realmente lo necesita. Abrir todo porque “así conecta” suele crear conflictos con servicios locales y revisiones de seguridad innecesarias.

Fallos frecuentes y verificación

Los fallos más comunes son ejecutar Claude Code como root, no fijar la versión del CLI, montar secretos de producción, iniciar servidores largos en postCreateCommand y publicar puertos de base de datos sin necesidad. Cada punto parece pequeño, pero juntos destruyen la reproducibilidad.

Después de reconstruir, comprueba node --version, npm --version, claude --version, un comando de lint o test y http://localhost:3000. Revisa que .env, .env.local y secrets/** no estén disponibles para Claude Code. Confirma que PostgreSQL y Redis no están expuestos fuera de lo necesario.

Para profundizar en servicios, lee la guía de Docker Compose con Claude Code. Para llevar la verificación a despliegues, continúa con la guía de CI/CD con Claude Code. Si tu equipo quiere convertir devcontainers, CLAUDE.md, permisos y revisión de artículos en un flujo repetible, revisa training / consultation.

Resultado de la prueba

En un repositorio pequeño de Next.js, lo que más redujo fricción fue separar el script de postCreateCommand, poner node_modules en un volume del contenedor y conservar /home/node/.claude en un volume por proyecto. Cada rebuild dejó la misma versión de Claude Code, el mismo flujo de instalación, la misma generación de Prisma y el mismo puerto 3000. El riesgo real apareció con el volume de la base de datos: después de varias migraciones de prueba, el estado viejo podía confundir el diagnóstico. La reproducibilidad no depende solo del Dockerfile, sino de versiones, secretos, volumes y puertos revisados juntos.

#Claude Code #Dev Container #VS Code #Docker #entorno de desarrollo
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.