SkillByAIOpen interactive version →

Lesson 20 / 25

compose.override.yaml for Development

Loaded automatically.

Automatic merge

If a compose.override.yaml exists next to compose.yaml, Compose loads and merges it automatically. Use it for development conveniences: published debug ports, bind-mounted source code, verbose logging. Merging rules: single values (like LOG_LEVEL) are replaced, lists such as ports are combined, and maps are merged key by key. Passing -f compose.yaml explicitly skips the automatic override.

One base, small differences

Override files, multiple -f flags and profiles keep environment differences explicit.

Figure 7.1 — Overrides, production files and profiles.

The override file

Development-only additions for the API.

services:
  api:
    ports:
      - "3000:3000"
    environment:
      LOG_LEVEL: debug
    volumes:
      - ./api:/app

With and without the override, run

I ran this with Docker Compose v2.38.1 and jq in the demo "shop" project. docker compose config parses, interpolates, merges and validates the files without starting containers; the Docker daemon was not running, so nothing was started. By default the API gets LOG_LEVEL debug, port 3000 and a bind mount from the override; loading compose.yaml alone gives LOG_LEVEL info, no published ports and no mounts.

echo "compose.yaml + compose.override.yaml (default):"
docker compose config --format json | jq -c ".services.api | {LOG_LEVEL: .environment.LOG_LEVEL, ports: [.ports[]?.published], volumes: [.volumes[]?.type]}"
echo "compose.yaml only:"
docker compose -f compose.yaml config --format json | jq -c ".services.api | {LOG_LEVEL: .environment.LOG_LEVEL, ports: [.ports[]?.published], volumes: [.volumes[]?.type]}"

Output:

compose.yaml + compose.override.yaml (default):
{"LOG_LEVEL":"debug","ports":["3000"],"volumes":["bind"]}
compose.yaml only:
{"LOG_LEVEL":"info","ports":[],"volumes":[]}

Quick check: When is compose.override.yaml loaded?

  • Only in production
  • Automatically, unless you pass -f files explicitly
  • Never; it must be named in every command
  • Only with --profile override
Answer

Automatically, unless you pass -f files explicitly — It exists for local development tweaks.