🚀 Docker Compose (PDR Avanzado): patrones pro para stacks reales
✅ Objetivo: Tener un PDR avanzado (sin repetir lo básico) con técnicas “pro” para que tu stack sea más seguro, mantenible, configurable y listo para equipos.
🧭 Índice
- 🧩 Múltiples archivos (
-f) y entornos (dev/stage/prod) - 🧱 Reutilización con anchors y
x-extends(DRY) - 🔐 Secrets & Configs (sin exponer credenciales)
- 🧰 Overrides por servicio:
command,entrypoint,profiles - 🧠
depends_onreal: salud, wait-for, y orden correcto - 🧯 Reinicios, límites, recursos, y políticas de estabilidad
- 📦 Build avanzado: multi-stage, cache,
build.args,target - 📡 Observabilidad: logging, health, métricas, eventos
- 🛡️ Hardening:
read_only,user, capabilities,security_opt - 🗃️ Volúmenes avanzados: bind vs named,
tmpfs, permisos - 🧪
docker composecomandos avanzados (debug pro) - ✅ Checklist final de producción (con Compose)
1) 🧩 Múltiples archivos Compose (dev / prod)
Idea: un compose.yml base + un compose.dev.yml (override) + compose.prod.yml.
📁 Ejemplo:
compose.yml
compose.dev.yml
compose.prod.yml
.env
.env.prod
▶️ Ejecutar en dev
docker compose -f compose.yml -f compose.dev.yml up -d --build▶️ Ejecutar en prod (en tu VPS)
docker compose -f compose.yml -f compose.prod.yml --env-file .env.prod up -d✅ Ventaja: no ensucias el base con detalles de dev (hot reload, puertos extra, tools, etc.).
2) 🧱 DRY con YAML Anchors y extensiones (x-)
Cuando tienes muchos servicios, repetir networks, restart, logging es un dolor.
Solución: anchors + x-commons.
x-common: &common
restart: unless-stopped
networks:
- app_net
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services:
api:
<<: *common
image: mi-api:latest
worker:
<<: *common
image: mi-worker:latest
networks:
app_net:3) 🔐 Secrets & Configs (sin meter claves en .env)
✅ Opción A: Secrets por archivo (recomendado)
📌 secrets: monta el valor como archivo dentro del contenedor.
services:
api:
image: mi-api:latest
secrets:
- db_password
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txtDentro de tu app, lees el archivo DB_PASSWORD_FILE.
✅ Opción B: .env (solo para dev)
- Bien para local
- No ideal para prod si hay credenciales sensibles
4) 🧰 command, entrypoint, profiles (control total)
Cambiar comando sin rebuild
services:
api:
image: mi-api:latest
command: ["sh", "-c", "python app.py --port 8080"]entrypoint para wrappers / init
entrypoint: ["sh", "-c", "./migrate.sh && exec "$@"", "--"]
command: ["node", "server.js"]profiles para herramientas de dev
services:
adminer:
image: adminer
profiles: ["dev"]
ports: ["8088:8080"]docker compose --profile dev up -d5) 🧠 Arranque correcto: health + “wait-for” (realista)
depends_on no garantiza que la DB esté lista para aceptar conexiones (solo controla arranque).
✅ Solución:
healthchecken DB- y en la API usar un “wait-for” (script) o retry interno.
Healthcheck en Postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 20Wait-for (bash) dentro de API (ejemplo)
#!/usr/bin/env sh
set -e
until nc -z db 5432; do
echo "⏳ esperando db..."
sleep 1
done
echo "✅ db lista"
exec "$@"6) 🧯 Estabilidad: restart, límites y recursos
Políticas de reinicio
| Política | Uso recomendado |
|---|---|
no | pruebas |
on-failure | jobs |
always | servicios core (cuidado) |
unless-stopped ✅ | la más práctica |
restart: unless-stoppedLímites (ojo: depende del runtime/entorno)
services:
api:
mem_limit: 512m
cpus: "0.50"7) 📦 Build avanzado (multi-stage + args + target)
Multi-stage (Dockerfile)
# stage build
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# stage runtime
FROM node:22-alpine
WORKDIR /app
COPY /app/dist ./dist
CMD ["node", "dist/index.js"]Compose build.args + target
services:
api:
build:
context: ./apps/api
dockerfile: Dockerfile
target: runtime
args:
NODE_ENV: production✅ Beneficio: imágenes más chicas, builds más rápidos, menos superficie de ataque.
8) 📡 Observabilidad: logs, eventos y health
Logging con rotación
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"Eventos en vivo (debug pro)
docker compose eventsVer health del contenedor
docker inspect --format='{{json .State.Health}}' mi_db9) 🛡️ Hardening (seguridad) para contenedores
Ejecutar como usuario no-root
user: "1000:1000"FS de solo lectura
read_only: true
tmpfs:
- /tmpQuitar privilegios (cuando aplique)
security_opt:
- no-new-privileges:true
cap_drop:
- ALL✅ Esto reduce riesgos si un contenedor es comprometido.
10) 🗃️ Volúmenes avanzados: bind vs named + permisos
Bind mount (código en dev)
volumes:
- ./apps/api:/appNamed volume (datos persistentes)
volumes:
- db_data:/var/lib/postgresql/datatmpfs (rápido y efímero)
tmpfs:
- /run
- /tmp11) 🧪 Comandos avanzados de docker compose
Validar y ver el Compose final (merge de overrides)
docker compose -f compose.yml -f compose.dev.yml configConvertir a salida “lista” para depurar
docker compose convertEjecutar un comando en servicio (sin entrar)
docker compose exec api envCorrer un contenedor “temporal”
docker compose run --rm api shEscalar (útil para stateless)
docker compose up -d --scale worker=312) ✅ Checklist pro (antes de subir a VPS)
✅ Secrets fuera del repo (./secrets/* en .gitignore)
✅ Overrides separados (dev/prod)
✅ Healthchecks en DB y servicios críticos
✅ Logs con rotación configurada
✅ Volúmenes correctos (data persistente)
✅ restart: unless-stopped en servicios core
✅ Usuario no-root + read_only cuando se pueda
✅ docker compose config sin warnings
✅ Puertos expuestos mínimos (solo lo necesario)
🧩 Figura: estrategia dev vs prod (simple)
DEV 🧪 PROD 🏭
compose.yml + compose.dev.yml compose.yml + compose.prod.yml
| |
hot reload + tools mínimos puertos + secrets + hardening
✅ Cierre
Con este PDR avanzado puedes montar stacks “de verdad”:
- configuración por entorno
- seguridad
- reutilización DRY
- observabilidad y estabilidad
- builds profesionales
Si me dices tu stack exacto (por ejemplo: Spring Boot + Postgres + Redis + Nginx + pgAdmin) te lo dejo totalmente adaptado con nombres reales, puertos y buenas prácticas específicas.