$docker.recipes
·10 min read·Updated February 2026

Managing Dev, Staging, and Production with Docker Compose Overrides

How to use Docker Compose override files and profiles to manage multiple environments from a single codebase without duplicating configuration.

docker-composeenvironmentsdevopsconfiguration

01The Multi-Environment Problem

A common mistake is maintaining separate docker-compose files for each environment — docker-compose.dev.yml, docker-compose.staging.yml, docker-compose.prod.yml. They start identical but gradually drift apart as changes are made to one but not the others. Eventually, a bug exists in production that can't be reproduced in development because the configurations are subtly different. Docker Compose has built-in features to solve this: override files and profiles. The pattern is simple: one base file defines the shared configuration, and small override files define only the differences. Let me show you how.

02Compose Override Files

Docker Compose automatically merges docker-compose.yml with docker-compose.override.yml if both exist. For multiple environments, use the -f flag to specify which files to merge:
[docker-compose.yml]
1# docker-compose.yml (base - shared across all environments)
2services:
3 app:
4 image: myapp:latest
5 depends_on:
6 db:
7 condition: service_healthy
8
9 db:
10 image: postgres:16-alpine
11 volumes:
12 - pgdata:/var/lib/postgresql/data
13 healthcheck:
14 test: ["CMD-SHELL", "pg_isready"]
15 interval: 5s
16 retries: 5
17
18volumes:
19 pgdata:

03Development Override

The dev override adds hot-reloading, debug ports, and relaxed resource limits:
[docker-compose.dev.yml]
1# docker-compose.dev.yml
2services:
3 app:
4 build: .
5 volumes:
6 - ./src:/app/src # Hot reload
7 ports:
8 - "3000:3000"
9 - "9229:9229" # Debug port
10 environment:
11 - NODE_ENV=development
12 - DEBUG=true
13
14 db:
15 ports:
16 - "5432:5432" # Expose for local tools
17 environment:
18 - POSTGRES_PASSWORD=devpassword

04Production Override

The prod override adds resource limits, security hardening, and production-specific config:
[docker-compose.prod.yml]
1# docker-compose.prod.yml
2services:
3 app:
4 restart: unless-stopped
5 environment:
6 - NODE_ENV=production
7 deploy:
8 resources:
9 limits:
10 memory: 1G
11 cpus: "2.0"
12 read_only: true
13 security_opt:
14 - no-new-privileges:true
15
16 db:
17 restart: unless-stopped
18 environment:
19 - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
20 secrets:
21 - db_password
22 deploy:
23 resources:
24 limits:
25 memory: 512M
26
27secrets:
28 db_password:
29 file: ./secrets/db_password.txt

05Using the Right Environment

Run with the appropriate overrides:
[terminal]
1# Development
2docker compose -f docker-compose.yml -f docker-compose.dev.yml up
3
4# Production
5docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
6
7# Shortcut: create a .env file with COMPOSE_FILE
8# echo "COMPOSE_FILE=docker-compose.yml:docker-compose.dev.yml" > .env
9# Now just: docker compose up

Set the COMPOSE_FILE environment variable in your shell profile or .env file so you don't have to pass -f flags every time. Use a colon separator for multiple files.

About the Author

Frank Pegasus

DevOps engineer and self-hosting enthusiast with over a decade of experience running containerized workloads in production. Creator of docker.recipes.