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.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
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.
FAQ
¿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
Slim Production Images with Multi-Stage Docker Builds
How to build minimal production Docker images using multi-stage builds with distroless base images, covering Go, Node.js, Python, and Java examples with image size reduction.
RecipeShare Workflow Logic with GitHub Actions Reusable Workflows
How to create and consume reusable workflows in GitHub Actions, covering inputs, secrets, conditional jobs, matrix strategy, and organization-wide sharing.
RecipeMount Configs and Secrets into Kubernetes Pods
How to mount ConfigMaps and Secrets into Kubernetes pods using env vars, volumes, projected volumes, and secret management with external secrets.
RecipeAcross Multiple OS and Language Versions with GitHub
How to use GitHub Actions matrix strategy to test across multiple operating systems, language versions, and configurations with include, exclude, and dynamic matrices.