Si ya sabes usar Docker para correr un contenedor individual, el siguiente paso natural es Docker Compose. En lugar de levantar cada contenedor por separado con comandos largos de docker run, defines todo en un archivo YAML y lo arrancas con un solo comando: docker compose up.
En esta guía vas a aprender Docker Compose desde cero — desde la instalación hasta desplegar una aplicación completa con base de datos, caché y volúmenes persistentes.
Requisitos previos
Necesitas tener Docker instalado en tu máquina. Docker Compose viene incluido en Docker Desktop (Mac y Windows) y en las versiones recientes del paquete docker-ce en Linux.
Verifica que lo tienes instalado:
docker compose version
Si ves algo como Docker Compose version v2.x.x, estás listo.
Docker Compose V2
Esta guía usa la sintaxis moderna docker compose (con espacio). La versión antigua docker-compose (con guión) está deprecada desde 2023. Si aún usas la versión con guión, actualiza tu instalación de Docker.
¿Qué es Docker Compose?
Docker Compose es una herramienta que permite definir y ejecutar aplicaciones multi-contenedor. En lugar de ejecutar cada contenedor manualmente, defines todos los servicios, redes y volúmenes en un archivo docker-compose.yml y los gestionas como una unidad.
| Sin Docker Compose | Con Docker Compose |
|---|---|
Un comando docker run por cada servicio | Un solo docker compose up |
| Redes creadas manualmente | Red creada automáticamente entre servicios |
| Variables de entorno pasadas en la línea de comandos | Variables definidas en archivo .env |
| Volúmenes gestionados individualmente | Volúmenes declarados y gestionados juntos |
| Reinicio manual si algo falla | restart: unless-stopped integrado |
Tu primer docker-compose.yml
Empecemos con un ejemplo mínimo: un servidor Nginx sirviendo una página estática.
# docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
Crea un directorio html con un archivo index.html:
mkdir html
echo "<h1>Hola desde Docker Compose</h1>" > html/index.html
Levanta el servicio:
docker compose up -d
Abre http://localhost:8080 y verás tu página. Para detener todo:
docker compose down
Eso es Docker Compose en su forma más simple. Ahora vamos con ejemplos reales.
Ejemplo 1: WordPress con MySQL
Este es uno de los usos más comunes. Un WordPress completo con su base de datos, todo definido en un archivo:
# docker-compose.yml
services:
wordpress:
image: wordpress:6
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
volumes:
- wp_data:/var/www/html
depends_on:
db:
condition: service_healthy
db:
image: mysql:8
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
wp_data:
db_data:
Crea un archivo .env con las contraseñas:
# .env
DB_PASSWORD=contraseña_segura_123
DB_ROOT_PASSWORD=root_seguro_456
docker compose up -d
Seguridad
Nunca pongas contraseñas directamente en el docker-compose.yml. Usa un archivo .env y agrégalo a tu .gitignore para que no se suba al repositorio.
Conceptos clave de este ejemplo
depends_on con healthcheck — WordPress no arranca hasta que MySQL esté saludable. Sin esto, WordPress podría intentar conectarse antes de que la base de datos esté lista y fallar.
volumes nombrados — wp_data y db_data son volúmenes persistentes. Si haces docker compose down, los datos se conservan. Si haces docker compose down -v, se eliminan los volúmenes y los datos.
restart: unless-stopped — Si el contenedor se cae o el servidor se reinicia, Docker lo levanta automáticamente. Solo se detiene si tú lo detienes manualmente.
Ejemplo 2: Aplicación con PostgreSQL y Redis
Un stack más realista para una aplicación empresarial — una API con PostgreSQL como base de datos y Redis como caché:
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/miapp
REDIS_URL: redis://cache:6379
NODE_ENV: production
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: miapp
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d miapp"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
volumes:
pg_data:
redis_data:
Conceptos nuevos
build — En lugar de usar una imagen pre-construida, Docker Compose construye la imagen desde tu Dockerfile. Cada vez que hagas docker compose build, se reconstruye con tus cambios.
command — Sobreescribe el comando por defecto del contenedor. Aquí configuramos Redis con un límite de memoria de 256 MB y política de evicción LRU (elimina las claves menos usadas cuando se llena).
Comandos esenciales
Estos son los comandos que vas a usar todos los días:
# Levantar todos los servicios en background
docker compose up -d
# Ver logs de todos los servicios
docker compose logs -f
# Ver logs de un servicio específico
docker compose logs -f db
# Detener todos los servicios (conserva datos)
docker compose down
# Detener y eliminar volúmenes (¡borra datos!)
docker compose down -v
# Reconstruir imágenes y levantar
docker compose up -d --build
# Ver estatus de los servicios
docker compose ps
# Ejecutar un comando dentro de un contenedor
docker compose exec db psql -U app -d miapp
# Escalar un servicio (levantar múltiples réplicas)
docker compose up -d --scale app=3
Redes en Docker Compose
Docker Compose crea automáticamente una red para tu stack. Todos los servicios definidos en el mismo docker-compose.yml pueden comunicarse entre sí usando el nombre del servicio como hostname.
En el ejemplo anterior, la aplicación se conecta a PostgreSQL usando db como hostname y a Redis usando cache. No necesitas IPs, no necesitas configurar nada — Docker Compose lo resuelve automáticamente.
┌─────────────────────────────────────────┐
│ Red: miapp_default │
│ │
│ ┌─────┐ ┌──────┐ ┌───────┐ │
│ │ app │───▶│ db │ │ cache │ │
│ │:3000│ │:5432 │ │:6379 │ │
│ └─────┘ └──────┘ └───────┘ │
│ │ │
│ ▼ │
│ Puerto 3000 expuesto al host │
└─────────────────────────────────────────┘
Si necesitas que dos stacks diferentes se comuniquen, puedes crear redes externas:
networks:
shared:
external: true
Variables de entorno y archivos .env
Docker Compose lee automáticamente el archivo .env en el mismo directorio que tu docker-compose.yml. Las variables se sustituyen con la sintaxis ${VARIABLE}:
# .env
DB_PASSWORD=mi_contraseña_segura
POSTGRES_VERSION=16
APP_PORT=3000
# docker-compose.yml
services:
db:
image: postgres:${POSTGRES_VERSION}-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
app:
ports:
- "${APP_PORT}:3000"
Para diferentes entornos (desarrollo, staging, producción), puedes tener archivos separados:
# Desarrollo (usa .env por defecto)
docker compose up -d
# Producción (usa .env.production)
docker compose --env-file .env.production up -d
Buenas prácticas
Después de usar Docker Compose en decenas de proyectos empresariales, estas son las prácticas que recomendamos:
Siempre usa volúmenes nombrados para datos persistentes — Los volúmenes anónimos son difíciles de rastrear y respaldar. Los nombrados son explícitos y aparecen en docker volume ls.
Siempre define healthchecks en bases de datos — Un depends_on sin healthcheck solo espera a que el contenedor arranque, no a que el servicio esté listo. La base de datos puede tardar varios segundos en aceptar conexiones después de arrancar.
Nunca pongas secretos en el docker-compose.yml — Usa .env para desarrollo y Docker Secrets o variables de entorno del sistema para producción.
Fija versiones de imagen — Usa postgres:16-alpine, no postgres:latest. Latest puede cambiar en cualquier momento y romper tu aplicación sin aviso.
Usa restart: unless-stopped — Tus contenedores se recuperan automáticamente de fallos y reinicios del servidor, pero puedes detenerlos manualmente cuando necesites.
Usa docker compose logs -f para debugging — Es tu primera herramienta de diagnóstico. Si algo no funciona, los logs te dirán qué pasó.
Siguientes pasos
Con Docker Compose dominado, el siguiente nivel es:
- Docker en producción — configurar TLS, backups automatizados de volúmenes y monitoreo con Prometheus y Grafana
- CI/CD — integrar
docker compose buildydocker compose pushen tu pipeline de GitHub Actions o GitLab CI - Kubernetes — cuando necesitas múltiples servidores, auto-scaling y alta disponibilidad real
- Proxmox — virtualización para aislar tus hosts de Docker en ambientes empresariales
Contenedores empresariales
¿Necesitas ayuda para containerizar tu aplicación?
Te ayudamos a diseñar la arquitectura de contenedores y Docker Compose para tu ERP, CRM o aplicación a medida.



