Skip to content

Docker Compose en 2026: buenas prácticas que realmente importan

txetxu
Published date:
Edit this post

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

La diferencia entre un compose.yml de 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)

Anterior
Vibe Coding: programar con IA a la velocidad del pensamiento
Siguiente
CSS moderno en 2026: container queries, :has() y anchor positioning