Skip to main content

Compose Override

A secondary Docker Compose file that extends or modifies the base compose file for specific environments. Docker automatically merges docker-compose.override.yml with docker-compose.yml, allowing environment-specific configuration without duplicating the base file.

Compose Override — Environment-Specific Configuration Without Duplication

What Is a Compose Override in Simple Terms?

Your development setup needs bind mounts for hot reloading. Your CI setup needs different port numbers. Your production setup needs resource limits. Instead of maintaining three completely separate compose files, you maintain one base file and small override files that add or change only what is different per environment.

Docker automatically merges docker-compose.override.yml with docker-compose.yml when both exist in the same directory.

Bash
docker-compose.yml <- base: what all environments share
docker-compose.override.yml <- automatically merged (dev by default)
docker-compose.prod.yml <- production overrides (specify with -f)
docker-compose.ci.yml <- CI overrides (specify with -f)

Example: Base + Override Pattern

YAML
# docker-compose.yml — base (shared across all environments)
version: "3.8"
services:
api:
image: payment-api:latest
environment:
NODE_ENV: production
DB_HOST: postgres
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 10s
volumes:
postgres-data:
YAML
# docker-compose.override.yml — development overrides (auto-merged)
version: "3.8"
services:
api:
build: . # build locally in dev
environment:
NODE_ENV: development
DEBUG: "*"
volumes:
- ./src:/app/src # hot reloading
ports:
- "3000:3000"
- "9229:9229" # debugger port
command: ["npm", "run", "dev"]
postgres:
ports:
- "5432:5432" # expose for local DB GUI
environment:
POSTGRES_PASSWORD: devpassword
YAML
# docker-compose.prod.yml — production overrides (use with -f)
version: "3.8"
services:
api:
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.5"
memory: 512M
logging:
driver: json-file
options:
max-size: "100m"
max-file: "3"

Using Override Files

Bash
# Development (docker-compose.override.yml auto-merged)
docker compose up -d
# Merges: docker-compose.yml + docker-compose.override.yml
# Production (explicit file selection)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Merges: base + prod overrides only
# CI (explicit file selection)
docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d
# See the fully merged result before running
docker compose config
# Shows the final merged configuration
Tip

Use docker compose config to preview the fully merged compose file before running it. This is the fastest way to verify your override files are being merged as expected — especially useful when debugging why a service has unexpected configuration.

Frequently Asked Questions

Why use a Compose override file instead of separate compose files per environment?

Docker Compose automatically merges docker-compose.override.yml on top of docker-compose.yml with no extra flag needed, so the base file stays the single source of truth for what a service fundamentally is, while the override file layers on environment-specific tweaks like volume mounts for local development or different port bindings. This avoids duplicating the entire service definition across multiple full compose files that inevitably drift out of sync.

What's a common override-file mistake?

Forgetting that Compose merges list-type keys like `ports` and `command` by full replacement in some cases and by append in others depending on the key, leading to unexpected duplicate port bindings or environment variables. When targeting a specific non-default file combination (e.g., a docker-compose.prod.yml instead of the auto-loaded override), you must explicitly pass `-f` for both files — Compose won't auto-merge a differently named file.