Orquestación multi-servicio con Docker Compose: entorno local en un solo comando

La tarde de mi primer día en el equipo, llevaba ya el quinto error en pantalla: puerto MySQL ocupado, Redis incompatible, RabbitMQ que no conectaba. Un compañero miró mi monitor y suspiró: “¿Cuánto llevas instalando?”
“Desde las nueve de la mañana.”
Cuatro horas para instalar tres bases de datos. Y aún faltaban ElasticSearch y MongoDB.
Ahí entendí que el entorno local puede ser un pozo sin fondo.
Luego el equipo adoptó Docker Compose. Un nuevo miembro clona el repo, docker-compose up -d, y en cinco minutos MySQL, Redis, RabbitMQ, API y Web están arriba. ¿Cambiar de proyecto? Otro directorio, otro compose. ¿Limpiar? docker-compose down -v y no queda rastro.
Este artículo cuenta ese cambio: cómo orquestar varios servicios con Docker Compose y pasar del infierno de configuración manual a un solo comando.
Por qué necesitas orquestación multi-servicio
Hace diez años, con apps monolíticas, bastaba instalar el JDK y apuntar a una base de datos. Hoy la mayoría de proyectos son microservicios o al menos frontend/backend separados.
Un entorno local típico incluye: frontend, API, MySQL, Redis, RabbitMQ… y a veces ElasticSearch o MongoDB.
El dolor de instalar a mano
Cada máquina repite el ritual. ¿MySQL 5.7 u 8.0? Versión incorrecta, SQL incompatible. Redis en 6379, ¿y si está ocupado? RabbitMQ exige Erlang… Solo RabbitMQ me costó media hora de documentación.
Después vienen conflictos de versión y puertos. Cambias de proyecto A a B: ambos quieren 3306. Reconfiguras, paras servicios, vuelves a A y otra vez.
La pesadilla del equipo
“En mi máquina funciona.”
El nuevo clona, instala, no arranca. MySQL 8.0 vs 5.7 del proyecto, Redis con o sin contraseña… Medio día perdido para poner en marcha el entorno.
La idea de Compose
Empaqueta todo en contenedores y unifica la configuración en un archivo. No te importa cómo se instala cada base de datos: defines servicios, puertos y variables, y arrancas. Cambiar de proyecto = otro compose. Limpiar = un comando.
Es pasar de montar un PC pieza a pieza a comprar uno listo para usar.
Configuración central de docker-compose.yml
Ejemplo con cuatro servicios: Web, API, MySQL y Redis.
# docker-compose.yml
version: "3.8" # Versión del archivo Compose; 3.8 cubre la mayoría de opciones
services:
# Frontend
web:
build: ./frontend # Construye imagen desde ./frontend
ports:
- "3000:3000" # host:contenedor
depends_on:
- api # api arranca antes
environment:
- API_URL=http://api:8080 # URL interna del backend
# Backend API
api:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
environment:
- DB_HOST=mysql # nombre del servicio, no localhost
- DB_PORT=3306
- DB_USER=root
- DB_PASSWORD=dev123 # en producción usa .env
- REDIS_HOST=redis
- REDIS_PORT=6379
# MySQL
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=dev123
- MYSQL_DATABASE=myapp
volumes:
- mysql_data:/var/lib/mysql
# Redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
mysql_data:
Campos clave
services: cada servicio puede usar build, image o ambos.
ports: "puerto_host:puerto_contenedor". Si 3306 está ocupado, usa "13006:3306".
depends_on: orden de arranque, con matices (ver abajo).
environment: variables de conexión. En producción no dejes contraseñas en texto plano.
volumes: persistencia; borrar el contenedor no borra el volumen.
Trampa habitual
El nombre del servicio es el hostname en la red interna. DB_HOST=mysql, no localhost. En el contenedor de la API, localhost es la propia API, no MySQL. La primera vez que escribí localhost:3306 no conectó hasta que usé el nombre del servicio.
Dependencias y orden de arranque
depends_on ordena contenedores, no garantiza que MySQL ya acepte conexiones. El contenedor puede estar up mientras el daemon aún inicializa.
Solución 1: healthcheck
services:
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
api:
depends_on:
mysql:
condition: service_healthy
Solución 2: reintentos en la aplicación
Más simple: reintentar la conexión con backoff exponencial. Node.js con pool de mysql2; Python con tenacity.
Personalmente prefiero reintentos en la app: menos YAML y suele bastar. Healthcheck para servicios muy lentos.
Solución 1: healthcheck (detalle)
services:
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
api:
depends_on:
mysql:
condition: service_healthy
Solución 2: reintentos en código
Node.js con pool mysql2:
const pool = mysql.createPool({
host: 'mysql',
port: 3306,
user: 'root',
password: 'dev123',
database: 'myapp',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
Python con tenacity:
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=2, max=10))
def connect_db():
return mysql.connector.connect(host='mysql', ...)
Estrategia multi-entorno
Dev, test y prod difieren en puertos expuestos, contraseñas y servicios externos. Compose resuelve esto con base + override.
Base (común)
# docker-compose.yml
version: "3.8"
services:
web:
build: ./frontend
api:
build: ./backend
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
mysql:
image: mysql:8.0
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7-alpine
volumes:
mysql_data:
Override de desarrollo
# docker-compose.override.yml
version: "3.8"
services:
web:
ports:
- "3000:3000"
api:
ports:
- "8080:8080"
environment:
- DEBUG=true
mysql:
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=dev123
redis:
ports:
- "6379:6379"
docker-compose up fusiona ambos archivos automáticamente.
Override de producción
# docker-compose.prod.yml
version: "3.8"
services:
web:
# sin ports; nginx como proxy
api:
environment:
- DB_HOST={{DB_HOST}}
- DB_PASSWORD={{DB_PASSWORD}}
mysql:
environment:
- MYSQL_ROOT_PASSWORD={{MYSQL_ROOT_PASSWORD}}
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Variables desde .env (no commitear):
environment:
- DB_HOST={{DB_HOST:-localhost}}
- DB_PASSWORD={{DB_PASSWORD:-dev123}}
Comandos esenciales
docker-compose up -d # arrancar en segundo plano
docker-compose ps # estado
Salida típica de docker-compose ps:
NAME COMMAND SERVICE STATUS PORTS
myapp-web-1 "npm start" web running 0.0.0.0:3000->3000/tcp
myapp-api-1 "node index.js" api running 0.0.0.0:8080->8080/tcp
myapp-mysql-1 "mysqld" mysql running 0.0.0.0:3306->3306/tcp
myapp-redis-1 "redis-server" redis running 0.0.0.0:6379->6379/tcp
docker-compose logs -f api # -f sigue el log en vivo
docker-compose down # parar; conserva volúmenes
docker-compose down -v # parar y borrar volúmenes
docker-compose build api
docker-compose up -d --build api
| Comando | Uso |
|---|---|
docker-compose up -d | Arrancar todo |
docker-compose ps | Ver estado |
docker-compose logs -f api | Logs de API |
docker-compose down | Parar contenedores |
docker-compose down -v | Parar y borrar datos |
docker-compose restart api | Reiniciar servicio |
docker-compose build api | Reconstruir imagen |
Cierre
| Operación | Manual | Compose |
|---|---|---|
| Onboarding | 4-8 horas | 5 minutos (clone + up) |
| Cambiar proyecto | Reconfigurar, parar servicios | Cambiar directorio |
| Limpiar | Desinstalar, buscar procesos | Un comando |
| Consistencia en equipo | Cada máquina distinta | Mismo YAML |
Si sigues instalando bases de datos a mano, prueba Compose con API + MySQL. Luego añade Redis, RabbitMQ y multi-entorno.
Commitea docker-compose.yml y docker-compose.override.yml con un README. Un comando y listo — más fiable que documentación que caduca.
Orquestación multi-servicio con Docker Compose
Orquesta Web, API, MySQL y Redis con Docker Compose para arrancar el entorno local con un comando
⏱️ Estimated time: 15 min
- 1
Step 1: Crear docker-compose.yml
Crea el archivo de configuración en la raíz del proyecto:
```yaml
version: "3.8"
services:
web:
build: ./frontend
ports: ["3000:3000"]
depends_on: [api]
api:
build: ./backend
ports: ["8080:8080"]
depends_on: [mysql, redis]
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: dev123
MYSQL_DATABASE: myapp
redis:
image: redis:7-alpine
```
Nota: entre contenedores usa el nombre del servicio (p. ej. DB_HOST=mysql), no localhost - 2
Step 2: Arrancar todos los servicios
En el directorio de docker-compose.yml ejecuta:
```bash
docker-compose up -d
```
• La primera vez tarda más al descargar imágenes
• Arranques posteriores reutilizan imágenes locales y son casi instantáneos
• Sin -d los logs ocupan la terminal - 3
Step 3: Verificar el estado
Comprueba que todos los contenedores estén en marcha:
```bash
docker-compose ps
```
• STATUS running indica normalidad
• Si aparece exited o error, revisa con:
```bash
docker-compose logs api
``` - 4
Step 4: Configurar multi-entorno (opcional)
Crea docker-compose.override.yml (desarrollo):
```yaml
version: "3.8"
services:
mysql:
ports: ["3306:3306"]
```
• Compose fusiona override automáticamente
• En producción usa -f:
```bash
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
``` - 5
Step 5: Limpiar el entorno
Detener y eliminar contenedores:
```bash
docker-compose down # conserva volúmenes
docker-compose down -v # elimina volúmenes (borra datos)
```
• down al cambiar de proyecto
• down -v si los datos están corruptos y quieres empezar de cero
FAQ
¿Qué diferencia hay entre docker-compose.yml y Dockerfile?
¿depends_on garantiza que el servicio esté listo?
¿Cómo accede un contenedor a servicios del host?
¿Dónde se guardan los datos? ¿Se pierden al borrar contenedores?
¿Cómo gestionar configuraciones dev/test/prod?
¿Qué hacer si un puerto está ocupado?
5 min de lectura · Publicado el: 9 abr 2026 · Actualizado el: 21 ago 2026
Guía práctica de Docker
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Acelerar builds de Docker: guía práctica para ir 10 veces más rápido con caché
Domina la caché por capas de Docker, la configuración de .dockerignore y la optimización del Dockerfile para pasar de 10 minutos a 30 segundos. Con ejemplos de código completos y caché montada de BuildKit.
Parte 7 de 38
Siguiente
Dependencias entre servicios en Docker Compose: healthcheck para resolver el orden de arranque de la base de datos
Guía detallada de depends_on y healthcheck en Docker Compose: ejemplos prácticos para evitar fallos de arranque cuando la base de datos aún no está lista, con plantillas completas para PostgreSQL y MySQL
Parte 9 de 38



Comentarios
Inicia sesión con GitHub para dejar un comentario