docker-compose up es el primer comando que aprendes. Lo que viene después —redes, secretos, healthchecks, perfiles para distintos entornos— es lo que separa una configuración funcional de una lista para producción.
Tabla de contenidos
Open Tabla de contenidos
Estructura base limpia
name: my-app
services:
api:
build:
context: .
dockerfile: Dockerfile
target: production # target de multi-stage
environment:
NODE_ENV: production
env_file: .env.production # nunca hardcodees credenciales
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy # esperar a que la DB esté lista
restart: unless-stopped
db:
image: postgres:17-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
postgres_data:
secrets:
db_password:
file: ./secrets/db_password.txtcompose.yml
Builds multi-stage: menos MB, más seguridad
Un Dockerfile de producción nunca debería incluir herramientas de desarrollo:
# Etapa 1: dependencias y build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # [!code highlight]
COPY . .
RUN npm run build
# Etapa 2: imagen final mínima
FROM node:22-alpine AS production # [!code ++]
WORKDIR /app # [!code ++]
# Copiar solo lo estrictamente necesario
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node # no ejecutar como root // [!code ++]
EXPOSE 3000
CMD ["node", "dist/server.js"]Dockerfile
La diferencia de tamaño puede ser de 600 MB → 80 MB.
Perfiles para distintos entornos
Con profiles puedes activar servicios según el contexto sin mantener múltiples archivos Compose:
services:
api:
# sin perfil = siempre activo
build: .
adminer:
image: adminer
profiles: [dev, debug] # solo en desarrollo
ports:
- "8080:8080"
prometheus:
image: prom/prometheus
profiles: [monitoring] # solo cuando lo necesites
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.ymlcompose.yml
# Levanta solo API + DB
docker compose up
# Levanta con herramientas de desarrollo
docker compose --profile dev up
# Stack completo de monitoreo
docker compose --profile monitoring up
Healthchecks que realmente funcionan
El depends_on básico solo espera a que el contenedor arranque, no a que el servicio esté listo. La diferencia importa:
services:
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s # periodo de gracia inicial
worker:
build: .
depends_on:
redis:
condition: service_healthy # espera al healthcheck en verdecompose.yml
Networking: aislamiento por defecto
Cada compose.yml crea su propia red. Para comunicar stacks separados:
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # sin acceso a internet
services:
nginx:
networks: [frontend, backend] # el único que toca ambas redes
api:
networks: [backend] # aislado del exterior
db:
networks: [backend] # ídemcompose.yml
Checklist antes de ir a producción
- Variables sensibles en
secretso.envfuera del repositorio - Build multi-stage activa
-
restart: unless-stoppeden todos los servicios críticos - Healthchecks configurados con un
start_periodadecuado -
depends_onconcondition: service_healthy - Usuarios no-root en los contenedores (
USER node,USER app) - Volúmenes nombrados para persistencia (nada de bind mounts en prod)
-
--max-old-space-sizeconfigurado acorde a la memoria del contenedor
La diferencia entre un
compose.ymlde tutorial y uno de producción no está en el número de líneas, sino en saber qué puede fallar y haberlo tenido en cuenta.
Traducido del original (Andrés Ujpán)