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.
docker-compose.yml <- base: what all environments sharedocker-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
# 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:# 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# 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
# 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 runningdocker compose config# Shows the final merged configurationTipUse
docker compose configto 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.