Listado de la etiqueta: compose

Docker Compose: el día que dejé de levantar contenedores a mano

Tenía un script Bash con 8 comandos docker run. Cada vez que alguien del equipo necesitaba levantar el entorno de desarrollo, le mandaba el script por Slack y rezaba para que no hubiera cambiado nada desde la última vez. Un día un compañero me mostró su docker-compose.yml. Nunca más volví al script.

¿Qué es Docker Compose?

Docker Compose es una herramienta para definir y ejecutar aplicaciones multi-contenedor usando un archivo YAML. En lugar de recordar 8 comandos docker run con todos sus flags, definís todos los servicios, redes y volúmenes en un solo archivo versionado. Un comando levanta todo; otro lo baja.

El docker-compose.yml completo: .NET + PostgreSQL + Redis + Nginx

Este es el stack que uso como base en mis proyectos. Cada servicio tiene su rol claro:

version: '3.8'

services:
  # Proxy inverso - único punto de entrada
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/certs:/etc/nginx/certs:ro
    depends_on:
      - api
    restart: unless-stopped

  # API .NET 8
  api:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__Default=Host=postgres;Database=miapp;Username=app;Password=${DB_PASSWORD}
      - Redis__ConnectionString=redis:6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    # Sin -p: solo accesible internamente a través de nginx

  # Base de datos PostgreSQL
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: miapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d miapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # Cache Redis
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    restart: unless-stopped

volumes:
  postgres-data:
  redis-data:

networks:
  default:
    name: miapp-network

El archivo .env: secretos fuera del YAML

# .env (en .gitignore - nunca en el repo)
DB_PASSWORD=password-super-seguro-aqui
REDIS_PASSWORD=otro-password-seguro

Los comandos que uso todos los días

# Levantar todo en background
docker compose up -d

# Levantar y ver los logs mientras arranca
docker compose up

# Levantar solo un servicio (y sus dependencias)
docker compose up -d api

# Ver estado de los servicios
docker compose ps

# Logs de todos los servicios
docker compose logs -f

# Logs de un servicio específico
docker compose logs -f api

# Ejecutar comando en un servicio
docker compose exec api bash
docker compose exec postgres psql -U app -d miapp

# Bajar todo (mantiene volúmenes)
docker compose down

# Bajar y eliminar volúmenes (¡CUIDADO en producción!)
docker compose down -v

# Rebuild y restart de un servicio
docker compose up -d --build api

# Escalar un servicio (múltiples instancias)
docker compose up -d --scale api=3

Health checks: que Compose espere a que los servicios estén listos

Uno de los problemas clásicos: la API arranca antes que la base de datos y falla al conectar. La solución está en los healthcheck y depends_on con condición, como hice en el ejemplo de Postgres. Compose espera hasta que el healthcheck pase antes de arrancar los servicios dependientes.

# Verificar el healthcheck de un servicio
docker compose ps
# NAME              STATUS
# miapp-postgres-1  healthy   ← Postgres superó el healthcheck
# miapp-api-1       running   ← API arrancó después

El antes y el después

Mi script Bash antes:

# ❌ Lo que tenía antes (8 líneas que siempre tenía que recordar actualizar)
docker network create miapp
docker run -d --name postgres --network miapp -e POSTGRES_PASSWORD=... ...
docker run -d --name redis --network miapp ...
docker run -d --name api --network miapp -e DB_HOST=postgres ...
docker run -d --name nginx --network miapp -p 80:80 ...
# etc...

Ahora:

# ✅ Todo el stack en un comando
docker compose up -d

El archivo está en el repo. Cualquier miembro del equipo puede clonar y levantar el entorno completo en un comando. Sin documentación de «cómo levantar el entorno». Sin scripts que se desactualizan. El docker-compose.yml es la documentación.


Artículo anterior: Redes en Docker | Serie Docker Completo | Próximo: Entornos consistentes →


Artículo anterior: Redes en Docker: de ‘no puedo conectar mis contenedores’ a entenderlo de verdad | Serie Docker Completo | Próximo: Cómo uso Docker para tener el mismo entorno en dev, test y producción →