Override Configs de Docker Compose por Entorno
Cómo usar Docker Compose override files para configuraciones específicas por entorno, cubriendo dev, test, staging, production, profiles y secrets management.
Overview
Docker Compose soporta múltiples archivos que se mergean juntos. El docker-compose.yml base define la estructura de servicios. Los override files lo customizan por entorno — diferentes ports, volumes, environment variables, resource limits, o incluso servicios adicionales. Compose mergea los archivos en orden: los archivos posteriores overridean o extienden los anteriores. Esto te permite mantener una config base y customizarla para dev, test, staging y production sin duplicación.
When to Use
- Desarrollo local con hot reload y debug tools
- Correr tests en aislamiento con configs específicas de test
- Staging/production con settings production-like (resource limits, sin debug)
- Cuando necesitás diferentes servicios por entorno (e.g., Mailhog en dev, SES en prod)
- Manejar aplicaciones multi-servicio a través de entornos
When NOT to Use
- Entorno único — un solo
docker-compose.ymles suficiente - Deployments de producción — usá Kubernetes o ECS, no Docker Compose
- Cuando las configs difieren drásticamente — archivos separados son más claros que overrides
- Cuando necesitás orquestación compleja — Compose es para dev/test, no producción
Solution
Compose file base
# docker-compose.yml — Configuración base
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- LOG_LEVEL=info
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
db-data:
Override de desarrollo
# docker-compose.override.yml — Usado por default con `docker compose up`
services:
app:
build:
target: dev # Multi-stage build dev target
ports:
- "3000:3000"
- "9229:9229" # Node.js debugger
environment:
- NODE_ENV=development
- LOG_LEVEL=debug
volumes:
- .:/app # Hot reload
- /app/node_modules # Prevenir host overwrite
command: npm run dev # Override entrypoint
db:
ports:
- "5432:5432" # Exponer para tools locales
environment:
POSTGRES_PASSWORD: devpassword
# Servicios solo de dev
mailhog:
image: mailhog/mailhog:latest
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
adminer:
image: adminer:latest
ports:
- "8080:8080"
depends_on:
- db
Override de producción
# docker-compose.prod.yml
services:
app:
build:
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- LOG_LEVEL=warn
- DATABASE_URL=postgres://myapp:${DB_PASSWORD}@db:5432/myapp
deploy:
replicas: 3
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.5"
memory: 256M
restart_policy:
condition: on-failure
max_attempts: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
db:
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
deploy:
resources:
limits:
cpus: "2.0"
memory: 2G
redis:
deploy:
resources:
limits:
cpus: "0.5"
memory: 256M
Override de test
# docker-compose.test.yml
services:
app:
build:
target: test
environment:
- NODE_ENV=test
- DATABASE_URL=postgres://myapp:testpass@db:5432/testdb
command: npm test
depends_on:
db:
condition: service_healthy
db:
environment:
POSTGRES_DB: testdb
POSTGRES_PASSWORD: testpass
# Sin volume — database ephemeral para tests
volumes: []
# Remover redis para tests
redis:
profiles:
- donotstart
Correr con diferentes overrides
# Desarrollo (default — usa docker-compose.yml + docker-compose.override.yml)
docker compose up
# Producción
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Test
docker compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit
# Build production images
docker compose -f docker-compose.yml -f docker-compose.prod.yml build
# Correr servicio específico
docker compose -f docker-compose.yml -f docker-compose.prod.yml up app
# Ver config mergeada
docker compose -f docker-compose.yml -f docker-compose.prod.yml config
Usar COMPOSE_FILE environment variable
# .env
COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml
# Ahora `docker compose up` usa ambos archivos
docker compose up -d
Compose profiles
# docker-compose.yml
services:
app:
# Siempre iniciado
image: my-app
db:
# Siempre iniciado
image: postgres:16
debug-tools:
# Solo iniciado con --profile debug
profiles:
- debug
image: nicolaka/netshoot
network_mode: "service:app"
load-test:
# Solo iniciado con --profile loadtest
profiles:
- loadtest
image: grafana/k6
volumes:
- ./tests:/scripts
# Start sin profiles
docker compose up -d # Solo app y db
# Start con debug profile
docker compose --profile debug up -d
# Start con load test profile
docker compose --profile loadtest up
# Start con múltiples profiles
docker compose --profile debug --profile loadtest up
Secrets management
# docker-compose.prod.yml
services:
app:
secrets:
- db-password
- api-key
environment:
- DB_PASSWORD_FILE=/run/secrets/db-password
- API_KEY_FILE=/run/secrets/api-key
db:
secrets:
- db-password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db-password
secrets:
db-password:
file: ./secrets/db-password.txt
api-key:
file: ./secrets/api-key.txt
Environment variables desde .env
# .env
DB_PASSWORD=supersecret
REDIS_URL=redis://redis:6379
JWT_SECRET=my-jwt-secret
# docker-compose.yml
services:
app:
environment:
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_URL=${REDIS_URL}
- JWT_SECRET=${JWT_SECRET}
Multi-file merge behavior
# docker-compose.yml (base)
services:
app:
image: my-app:latest
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- LOG_LEVEL=info
# docker-compose.override.yml
services:
app:
# Ports se mergean (no se reemplazan)
ports:
- "9229:9229" # Agregado a 3000:3000
# Environment se mergea
environment:
- NODE_ENV=development # Overridea production
- DEBUG=true # Agregado
Variants
Usar extends para reusar definiciones de servicios
# docker-compose.yml
services:
web:
extends:
file: docker-compose.base.yml
service: app
environment:
- APP_ROLE=web
worker:
extends:
file: docker-compose.base.yml
service: app
environment:
- APP_ROLE=worker
ports: [] # Sin ports para worker
Docker Compose con healthcheck dependencies
services:
app:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
Watch mode para desarrollo
# docker-compose.override.yml
services:
app:
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package.json
docker compose watch # Auto-sync changes y rebuild
Best Practices
-
For a deeper guide, see Docker Compose Dev/Prod Split: Separate Environments.
-
Mantené
docker-compose.ymlcomo base — no pongas config específica de entorno en él -
Usá
docker-compose.override.ymlpara dev — Compose lo pikea automáticamente -
Usá flags
-fexplícitos para entornos non-dev — previene accidental dev overrides -
Usá
.envpara secrets — no hardcodees passwords en YAML -
Usá
profilespara servicios opcionales — evita iniciar todo cuando no lo necesitás -
Usá
depends_onconcondition— asegura que los servicios inicien en el orden correcto -
Usá
healthcheckpara databases — previene que la app conecte antes de que DB esté listo -
Corré
docker compose configpara verificar el output mergeado — atrapa merge issues
Common Mistakes
- Olvidar que override.yml es automático:
docker compose upsiempre mergeadocker-compose.override.ymlsi existe. Usá-fpara ser explícito. - Hardcodear secrets en YAML: las passwords en
docker-compose.ymlterminan en git. Usá.envo Docker secrets. - No usar healthchecks: la app inicia antes de que DB esté listo, causando connection errors. Agregá healthchecks a servicios dependientes.
- Usar Compose para producción: Compose está diseñado para dev/test. Usá Kubernetes, ECS o Docker Swarm para orquestación de producción.
- No limpiar volumes:
docker compose downmantiene los volumes. Usádocker compose down -vpara remover data.
Preguntas frecuentes
¿Cómo mergea Docker Compose los archivos?
Hace deep-merge: listas (ports, volumes) se concatenan, maps (environment, labels) se mergean con valores posteriores overrideando los anteriores, y escalares (image, command) se reemplazan.
¿Cuál es el override file default?
docker-compose.override.yml. Si existe en el mismo directorio, Compose lo mergea automáticamente con docker-compose.yml cuando corrés docker compose up.
¿Puedo usar múltiples override files?
Sí. Usá múltiples flags -f: docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.secrets.yml up.
¿Qué son los Compose profiles?
Una forma de marcar servicios como opcionales. Los servicios con una lista profiles solo inician cuando pasás --profile <name>. Esto te permite mantener debug/test tools en el mismo archivo sin siempre iniciarlos.
¿Cómo paso environment variables?
Usá un archivo .env en el mismo directorio. Compose lo lee automáticamente. Referenciá variables con ${VAR_NAME} en el YAML.
Recursos Relacionados
Imágenes de Producción Slim con Multi-Stage Docker
Cómo construir imágenes Docker de producción minimales usando multi-stage builds con distroless base images, cubriendo Go, Node.js, Python y Java con reducción de tamaño.
RecipeCompartir Lógica de Workflows con GitHub Actions
Cómo crear y consumir reusable workflows en GitHub Actions, cubriendo inputs, secrets, jobs condicionales, matrix strategy y sharing a nivel organización.
RecipeMontar Configs y Secrets en Pods de Kubernetes
Cómo montar ConfigMaps y Secrets en pods de Kubernetes usando env vars, volumes, projected volumes y secret management con external secrets.
RecipeTestear a Través de Múltiples OS y Versiones con GitHub
Cómo usar GitHub Actions matrix strategy para testear a través de múltiples sistemas operativos, versiones de lenguajes y configuraciones con include, exclude y matrices dinámicas.