Docker Compose: Guía Completa con Ejemplos Prácticos
Docker Compose simplifica la orquestación de aplicaciones multi-contenedor al definir y ejecutar aplicaciones complejas usando un único archivo de configuración YAML. Esta guía completa cubre todo desde conceptos básicos hasta despliegues de producción avanzados con ejemplos del mundo real.
Introducción a Docker Compose
Docker Compose es una herramienta para definir y ejecutar aplicaciones Docker multi-contenedor. En lugar de gestionar contenedores individualmente con extensos comandos docker run, Compose usa un archivo YAML para configurar todos los servicios, redes y volúmenes de la aplicación en un solo lugar.
¿Por Qué Usar Docker Compose?
- Gestión Simplificada: Define el stack completo de la aplicación en un archivo
- Reproducibilidad: Comparte configuraciones con tu equipo
- Eficiencia de Desarrollo: Inicia el stack completo con un solo comando
- Consistencia de Entorno: Misma configuración en dev, test y staging
- Dependencias de Servicios: Define orden de inicio y dependencias
- Escalado Fácil: Escala servicios hacia arriba o abajo con un comando
Docker Compose vs Kubernetes
- Docker Compose: Desarrollo de host único y despliegues pequeños
- Kubernetes: Orquestación de producción multi-host a escala
Requisitos Previos
Antes de comenzar, asegúrate de tener:
- Docker Engine instalado (versión 20.10 o superior)
- Docker Compose instalado (versión 2.0 o superior)
- Conocimiento básico de Docker (imágenes, contenedores, volúmenes)
- Editor de texto para archivos YAML
- Comprensión de la arquitectura de tu aplicación
Verifica la instalación:
# Check Docker Compose version
docker compose version
# Or older standalone version
docker-compose --version
Notas de Instalación
Docker Compose v2 ahora está integrado como plugin de Docker CLI:
# New syntax (recommended)
docker compose up
# Old syntax (standalone)
docker-compose up
Esta guía usa la nueva sintaxis docker compose.
Conceptos Básicos de Docker Compose
Creando Tu Primer Archivo Compose
Crea un archivo llamado docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
Inicia la aplicación:
docker compose up
Detén la aplicación:
docker compose down
Flujo de Trabajo Básico
# Start services
docker compose up -d
# View running services
docker compose ps
# View logs
docker compose logs
# Stop services
docker compose down
Estructura del Archivo Compose
Especificación de Versión
# Compose file format version
version: '3.8'
Nota: La versión 3.8 es ampliamente soportada. Versiones más nuevas (3.9, 3.10) agregan características.
Claves de Nivel Superior
version: '3.8'
services: # Define containers
service1:
service2:
networks: # Define networks
network1:
volumes: # Define volumes
volume1:
configs: # Define configs (Swarm mode)
config1:
secrets: # Define secrets (Swarm mode)
secret1:
Ejemplo Mínimo
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
Configuración de Servicios
Servicios Basados en Imagen
services:
database:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
Servicios Basados en Construcción
services:
app:
build: . # Build from current directory
ports:
- "3000:3000"
api:
build:
context: ./api # Build context
dockerfile: Dockerfile.prod # Custom Dockerfile
args: # Build arguments
NODE_ENV: production
Nomenclatura de Contenedores
services:
web:
container_name: my-web-server
image: nginx:alpine
Mapeo de Puertos
services:
web:
image: nginx
ports:
- "8080:80" # host:container
- "8443:443"
- "127.0.0.1:9000:9000" # bind to specific interface
- "3000-3005:3000-3005" # port range
Variables de Entorno
services:
app:
image: node:18-alpine
environment:
NODE_ENV: production
API_KEY: ${API_KEY} # From host environment
DATABASE_URL: postgres://db:5432/mydb
Archivos de Entorno
services:
app:
image: node:18-alpine
env_file:
- .env
- .env.production
Archivo .env:
NODE_ENV=production
API_KEY=your_api_key_here
DATABASE_URL=postgres://db:5432/mydb
Volúmenes
services:
app:
image: node:18-alpine
volumes:
- ./app:/usr/src/app # bind mount
- app-data:/app/data # named volume
- /var/run/docker.sock:/var/run/docker.sock:ro # read-only
Depends On
services:
web:
image: nginx
depends_on:
- api
- cache
api:
image: node:18-alpine
depends_on:
- database
database:
image: postgres:15-alpine
cache:
image: redis:alpine
Políticas de Reinicio
services:
web:
image: nginx
restart: always # always, no, on-failure, unless-stopped
Verificaciones de Salud
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Límites de Recursos
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
Command y Entrypoint
services:
app:
image: node:18-alpine
command: npm start
# Or with array syntax
command: ["npm", "run", "dev"]
worker:
image: node:18-alpine
entrypoint: /docker-entrypoint.sh
Directorio de Trabajo
services:
app:
image: node:18-alpine
working_dir: /usr/src/app
Usuario
services:
app:
image: node:18-alpine
user: "1000:1000"
Redes en Compose
Red Predeterminada
Docker Compose crea automáticamente una red predeterminada:
version: '3.8'
services:
web:
image: nginx
api:
image: node:18-alpine
Los servicios pueden comunicarse usando nombres de servicios como nombres de host.
Redes Personalizadas
version: '3.8'
services:
web:
image: nginx
networks:
- frontend
api:
image: node:18-alpine
networks:
- frontend
- backend
database:
image: postgres
networks:
- backend
networks:
frontend:
backend:
Configuración de Red
networks:
frontend:
driver: bridge
backend:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
gateway: 172.28.0.1
Redes Externas
networks:
existing-network:
external: true
name: my-pre-existing-network
Volúmenes en Compose
Volúmenes Nombrados
version: '3.8'
services:
database:
image: postgres:15-alpine
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Configuración de Volumen
volumes:
db-data:
driver: local
driver_opts:
type: none
device: /path/on/host
o: bind
Volúmenes Externos
volumes:
existing-volume:
external: true
name: my-pre-existing-volume
Variables de Entorno
Sustitución de Variables
services:
web:
image: nginx:${NGINX_VERSION:-latest}
ports:
- "${WEB_PORT:-8080}:80"
Archivo .env
Crea .env en el mismo directorio que docker-compose.yml:
NGINX_VERSION=alpine
WEB_PORT=8080
POSTGRES_PASSWORD=secretpassword
Múltiples Archivos de Entorno
services:
app:
image: myapp
env_file:
- ./common.env
- ./prod.env
Comandos de Docker Compose
Iniciar Servicios
# Start all services
docker compose up
# Start in detached mode
docker compose up -d
# Start specific services
docker compose up web database
# Force recreate containers
docker compose up --force-recreate
# Build images before starting
docker compose up --build
Detener Servicios
# Stop all services
docker compose stop
# Stop specific service
docker compose stop web
# Stop and remove containers, networks
docker compose down
# Remove volumes too
docker compose down -v
# Remove images too
docker compose down --rmi all
Ver Estado
# List running services
docker compose ps
# Show all services (including stopped)
docker compose ps -a
# View service logs
docker compose logs
# Follow logs
docker compose logs -f
# Logs for specific service
docker compose logs -f web
# Show last 100 lines
docker compose logs --tail=100
Ejecutar Comandos
# Execute command in service
docker compose exec web sh
# Run one-off command
docker compose run web npm install
# Run without dependencies
docker compose run --no-deps web npm test
Construir Imágenes
# Build all services
docker compose build
# Build specific service
docker compose build web
# Build without cache
docker compose build --no-cache
# Build with parallel execution
docker compose build --parallel
Escalar Servicios
# Scale specific service
docker compose up -d --scale web=3
# Scale multiple services
docker compose up -d --scale web=3 --scale worker=5
Validación
# Validate compose file
docker compose config
# Render compose file with variables
docker compose config --resolve-image-digests
Otros Comandos Útiles
# Pause services
docker compose pause
# Unpause services
docker compose unpause
# Restart services
docker compose restart
# View resource usage
docker compose top
# Pull latest images
docker compose pull
Ejemplos del Mundo Real
Ejemplo 1: Stack WordPress
version: '3.8'
services:
wordpress:
image: wordpress:latest
container_name: wordpress
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: database
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-data:/var/www/html
depends_on:
- database
networks:
- wp-network
database:
image: mysql:8.0
container_name: wordpress-db
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- db-data:/var/lib/mysql
networks:
- wp-network
phpmyadmin:
image: phpmyadmin:latest
container_name: phpmyadmin
restart: unless-stopped
ports:
- "8081:80"
environment:
PMA_HOST: database
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
depends_on:
- database
networks:
- wp-network
volumes:
wordpress-data:
db-data:
networks:
wp-network:
driver: bridge
Ejemplo 2: Aplicación Full Stack Node.js
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: react-frontend
restart: unless-stopped
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:5000
volumes:
- ./frontend:/app
- /app/node_modules
networks:
- app-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: node-backend
restart: unless-stopped
ports:
- "5000:5000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@database:5432/myapp
- REDIS_URL=redis://redis:6379
volumes:
- ./backend:/app
- /app/node_modules
depends_on:
- database
- redis
networks:
- app-network
database:
image: postgres:15-alpine
container_name: postgres-db
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
redis:
image: redis:7-alpine
container_name: redis-cache
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
nginx:
image: nginx:alpine
container_name: nginx-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- frontend
- backend
networks:
- app-network
volumes:
postgres-data:
redis-data:
networks:
app-network:
driver: bridge
Ejemplo 3: Microservicios con Cola de Mensajes
version: '3.8'
services:
api-gateway:
build: ./api-gateway
ports:
- "8080:8080"
environment:
- USER_SERVICE_URL=http://user-service:3001
- ORDER_SERVICE_URL=http://order-service:3002
depends_on:
- user-service
- order-service
networks:
- microservices
user-service:
build: ./user-service
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@postgres:5432/users
- RABBITMQ_URL=amqp://rabbitmq:5672
depends_on:
- postgres
- rabbitmq
networks:
- microservices
order-service:
build: ./order-service
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@postgres:5432/orders
- RABBITMQ_URL=amqp://rabbitmq:5672
depends_on:
- postgres
- rabbitmq
networks:
- microservices
postgres:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- microservices
rabbitmq:
image: rabbitmq:3-management-alpine
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
volumes:
- rabbitmq-data:/var/lib/rabbitmq
networks:
- microservices
volumes:
postgres-data:
rabbitmq-data:
networks:
microservices:
driver: bridge
Ejemplo 4: Aplicación Python Django
version: '3.8'
services:
web:
build: .
command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/code
- static-volume:/code/staticfiles
- media-volume:/code/media
expose:
- 8000
environment:
- DEBUG=0
- SECRET_KEY=${SECRET_KEY}
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/myapp
- REDIS_URL=redis://redis:6379/1
depends_on:
- db
- redis
networks:
- django-network
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- static-volume:/code/staticfiles:ro
- media-volume:/code/media:ro
ports:
- "80:80"
depends_on:
- web
networks:
- django-network
db:
image: postgres:15-alpine
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=myapp
networks:
- django-network
redis:
image: redis:7-alpine
networks:
- django-network
celery:
build: .
command: celery -A myproject worker -l info
volumes:
- .:/code
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/myapp
- REDIS_URL=redis://redis:6379/1
depends_on:
- db
- redis
networks:
- django-network
celery-beat:
build: .
command: celery -A myproject beat -l info
volumes:
- .:/code
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/myapp
- REDIS_URL=redis://redis:6379/1
depends_on:
- db
- redis
networks:
- django-network
volumes:
postgres-data:
static-volume:
media-volume:
networks:
django-network:
driver: bridge
Ejemplo 5: Stack de Monitoreo (Prometheus + Grafana)
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
ports:
- "9090:9090"
networks:
- monitoring
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
- GF_INSTALL_PLUGINS=grafana-piechart-panel
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
ports:
- "3000:3000"
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
ports:
- "9100:9100"
networks:
- monitoring
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8080:8080"
networks:
- monitoring
volumes:
prometheus-data:
grafana-data:
networks:
monitoring:
driver: bridge
Mejores Prácticas de Producción
Usar Control de Versiones
Siempre confirma docker-compose.yml en git:
git add docker-compose.yml .env.example
git commit -m "Add Docker Compose configuration"
Archivos Específicos de Entorno
# docker-compose.yml (base)
version: '3.8'
services:
web:
image: myapp
# docker-compose.override.yml (development)
version: '3.8'
services:
web:
volumes:
- .:/app
command: npm run dev
# docker-compose.prod.yml (production)
version: '3.8'
services:
web:
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Ejecuta con configuración específica:
# Development (uses override automatically)
docker compose up
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Mejores Prácticas de Seguridad
services:
app:
image: myapp
# Run as non-root user
user: "1000:1000"
# Read-only root filesystem
read_only: true
tmpfs:
- /tmp
# Drop capabilities
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# Security options
security_opt:
- no-new-privileges:true
Límites de Recursos
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
Verificaciones de Salud
services:
app:
image: myapp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
Configuración de Logging
services:
app:
image: myapp
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "production"
Usar Secrets (Docker Swarm)
version: '3.8'
services:
app:
image: myapp
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
Solución de Problemas
Ver Logs de Servicio
# All services
docker compose logs
# Specific service
docker compose logs web
# Follow logs
docker compose logs -f
# Last 100 lines
docker compose logs --tail=100
El Contenedor No Inicia
# Check status
docker compose ps
# View logs
docker compose logs service-name
# Validate compose file
docker compose config
# Force recreate
docker compose up --force-recreate
Problemas de Red
# Inspect network
docker network ls
docker network inspect project_default
# Recreate network
docker compose down
docker compose up
Problemas de Permisos de Volumen
# Check volume
docker volume inspect project_volume-name
# Fix permissions in container
docker compose exec service-name chown -R user:group /path
Limpiar Recursos
# Stop and remove everything
docker compose down
# Remove volumes too
docker compose down -v
# Remove images
docker compose down --rmi all
# Complete cleanup
docker compose down -v --rmi all --remove-orphans
Depurar Servicio
# Execute shell in running container
docker compose exec service-name sh
# Run one-off command
docker compose run --rm service-name sh
# View container processes
docker compose top service-name
Conclusión
Docker Compose optimiza la gestión de aplicaciones multi-contenedor, haciéndolo esencial para flujos de trabajo de desarrollo modernos. Esta guía cubrió todo desde conceptos básicos hasta configuraciones listas para producción.
Conclusiones Clave
- Configuración Única: Define el stack completo en
docker-compose.yml - Dependencias de Servicios: Usa
depends_onpara orden de inicio - Aislamiento de Red: Creación automática de red con descubrimiento de servicios
- Gestión de Volúmenes: Persiste datos con volúmenes nombrados y bind
- Flexibilidad de Entorno: Usa archivos
.envy sustitución de variables - Listo para Producción: Implementa verificaciones de salud, logging y límites de recursos
Referencia Rápida
# Essential Commands
docker compose up -d # Start services
docker compose down # Stop services
docker compose ps # List services
docker compose logs -f # View logs
docker compose exec web sh # Access shell
docker compose build # Build images
docker compose pull # Pull images
docker compose restart # Restart services
# Management
docker compose config # Validate file
docker compose up --build # Rebuild and start
docker compose down -v # Remove with volumes
docker compose scale web=3 # Scale service
Próximos Pasos
- Practica: Crea archivos compose para tus proyectos
- Optimiza: Implementa construcciones multi-etapa en Dockerfiles
- Asegura: Agrega verificaciones de salud y opciones de seguridad
- Monitorea: Integra soluciones de logging y monitoreo
- Orquesta: Avanza a Kubernetes para producción
- Automatiza: Integra con pipelines CI/CD
- Documenta: Mantén README completo con instrucciones de configuración
Docker Compose es perfecto para entornos de desarrollo y despliegues de host único. Para orquestación de producción multi-host, considera Kubernetes o Docker Swarm.


