Claude Code y Docker: Compose, volúmenes, redes y builds en CI
Crea Dockerfiles, Compose y builds de imagen en CI con Claude Code de forma segura.
Decide qué debe hacer Claude Code
Si pides a Claude Code “dockeriza esta app” sin límites, puede generar un Dockerfile que arranca una vez pero que nadie quiere revisar. Docker empaqueta el entorno de ejecución de una aplicación: una imagen es la plantilla, un contenedor es la instancia en ejecución, un volumen guarda datos fuera del contenedor y una red permite que los servicios se llamen por nombre.
En un proyecto real no basta con mirar el Dockerfile. Hay que revisar juntos Dockerfile, docker-compose.yml, .dockerignore, variables de entorno, health checks y build en CI. Mantén cerca las referencias oficiales: Dockerfile reference, Compose file reference, Docker build best practices, Docker Build GitHub Actions y Claude Code overview.
Para el flujo completo, combina esta guía con Claude Code CI/CD setup, approval and sandbox guide y security best practices.
Casos de uso concretos
La integración con Docker sirve cuando elimina dudas de entorno. Antes de editar, escribe qué problema debe resolver.
| Caso | Qué estandariza Docker | Qué pedir a Claude Code |
|---|---|---|
| Onboarding de nuevos devs | Node.js, PostgreSQL, Redis, puertos y comandos | Hacer que docker compose up arranque todo |
| Debug cercano a producción | Variables, nombres de servicio, health checks, orden de inicio | Comparar diferencias entre local y producción |
| Validación de PR | Dockerfile, lockfile, cache, logs de CI | Añadir workflow que solo construya la imagen |
| Formación y handoff | Comandos, fallos, recuperación | Documentar reglas en CLAUDE.md y README |
En un SaaS o sitio monetizado, esto toca ingresos. Checkout, formularios, paneles internos y páginas de producto no pueden depender de “mi laptop funciona”. Si trabajas solo, empieza por ClaudeCodeLab products. Si tu equipo necesita reglas por repositorio, permisos, CI y criterios de review, usa Claude Code training and consultation.
Prompt con límites claros
El primer prompt debe incluir contexto, archivos esperados, restricciones y verificación. Así evitas una configuración bonita pero insegura.
Dockerize this Node.js + TypeScript API.
Context:
- pnpm is the package manager.
- The app listens on port 3000.
- GET /health returns 200.
- Local development needs PostgreSQL 16 and Redis 7.
Create:
- Production multi-stage Dockerfile
- docker-compose.yml for local development
- .dockerignore
- Docker-related package.json scripts
- GitHub Actions workflow that only verifies docker build
Constraints:
- Do not run the production container as root.
- Do not COPY .env files or secrets into the image.
- Explain the volume and network choices for the README.
- Include verification commands and how to inspect failures.
Un multi-stage build separa el entorno de compilación del entorno de ejecución. Las herramientas de TypeScript quedan en el stage de build, y la imagen final recibe solo dependencias de producción y el directorio dist.
Dockerfile listo para copiar
Este ejemplo asume una API Node.js que compila src/index.ts en dist/index.js. El stage dev sirve para Compose local y runner para producción.
# syntax=docker/dockerfile:1
FROM node:22-bookworm-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
WORKDIR /app
RUN corepack enable
FROM base AS dev
ENV NODE_ENV=development
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
CMD ["pnpm", "dev"]
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
FROM deps AS build
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM base AS prod-deps
ENV NODE_ENV=production
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod && pnpm store prune
FROM base AS runner
ENV NODE_ENV=production
RUN groupadd --system --gid 1001 nodejs \
&& useradd --system --uid 1001 --gid nodejs appuser
COPY --from=prod-deps --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=appuser:nodejs /app/dist ./dist
COPY --chown=appuser:nodejs package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD node -e "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "dist/index.js"]
Pide a Claude Code que revise tres cosas: si un secreto puede entrar en la imagen, si el orden de COPY aprovecha la cache y si la imagen final no incluye devDependencies.
Compose: volúmenes y redes
Compose levanta varios contenedores juntos. En este stack, API, PostgreSQL y Redis comparten una red; la API se conecta a postgres y redis por nombre de servicio. Los volúmenes conservan datos fuera del contenedor. pgdata guarda la base de datos y api_node_modules evita que node_modules del host rompa dependencias Linux.
services:
api:
build:
context: .
target: dev
command: pnpm dev
ports:
- "3000:3000"
environment:
NODE_ENV: development
DATABASE_URL: postgres://app:app@postgres:5432/app
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- .:/app
- api_node_modules:/app/node_modules
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 10s
timeout: 3s
retries: 5
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
volumes:
pgdata:
api_node_modules:
depends_on ordena el arranque, pero no garantiza que la base de datos acepte conexiones. Por eso se combina con healthcheck y condition: service_healthy.
Mantén secretos fuera del build
Empieza con .dockerignore. Reduce el contexto de build y evita copiar .env.
.git
node_modules
dist
coverage
.env
.env.*
!.env.example
npm-debug.log*
pnpm-debug.log*
Dockerfile*
docker-compose*.yml
Después deja scripts compartidos en package.json.
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.json",
"start": "node dist/index.js",
"docker:dev": "docker compose up --build",
"docker:check": "bash scripts/docker-check.sh"
},
"dependencies": {
"@fastify/redis": "latest",
"fastify": "latest",
"pg": "latest"
},
"devDependencies": {
"tsx": "latest",
"typescript": "latest"
}
}
Script de verificación repetible
No confíes en comandos manuales. Guarda el flujo en scripts/docker-check.sh y úsalo también en handoffs.
#!/usr/bin/env bash
set -euo pipefail
docker compose build api
docker compose up -d postgres redis api
cleanup() {
docker compose down
}
trap cleanup EXIT
for attempt in {1..30}; do
if curl -fsS http://127.0.0.1:3000/health >/dev/null; then
echo "healthcheck ok"
exit 0
fi
echo "waiting for api... ${attempt}/30"
sleep 2
done
docker compose logs api
exit 1
El script construye la imagen, arranca el stack, espera /health y muestra logs si la API nunca queda lista.
CI: primero build, luego push
En el primer PR verifica que la imagen se construye sin publicar nada. Firma, scanning y SBOM pueden venir después.
name: docker-build
on:
pull_request:
push:
branches:
- main
jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v7
with:
context: .
target: runner
push: false
tags: claude-code-docker-sample:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
Claude Code debe poder explicar si el workflow hace push, si los permisos son excesivos y qué pasa con una cache vacía.
Fallos frecuentes
El fallo más caro es copiar .env dentro de la imagen. Si un secreto entra en el registry, la limpieza es lenta. Revisa .dockerignore, secrets de CI y logs de build a la vez.
Otro fallo común es montar node_modules del host dentro de un contenedor Linux. Los módulos nativos pueden cambiar entre macOS, Windows y Linux. Un volumen con nombre evita mezclar mundos.
También aparece la carrera de arranque: la API se inicia antes de que PostgreSQL acepte conexiones. Usa health checks y retries de aplicación.
Por último, no ejecutes producción como root. Funciona en demos, pero aumenta el riesgo. Pide a Claude Code que señale las líneas exactas que demuestran el usuario no-root.
Flujo seguro de trabajo
El orden recomendable es: inspeccionar el repositorio, listar runtime dependencies, escribir .dockerignore, crear Compose local, crear Dockerfile de producción, añadir script de verificación y finalmente CI. Después de cada paso, lee git diff y pide a Claude Code un recibo breve: archivos cambiados, razón, comando de verificación y riesgo restante.
Este flujo encaja con session handoff template. Docker no debería ser un archivo generado una vez, sino un hábito revisable del equipo.
Resultado práctico
En la práctica, dividir el trabajo mejoró mucho la calidad. .dockerignore, luego Compose, luego health checks y después CI produjo diffs más fáciles de revisar. El volumen api_node_modules y la espera de service_healthy redujeron dos problemas típicos: “solo funciona en mi máquina” y “la API arrancó pero no conecta con Postgres”.
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.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.