The compose file that became three compose files

One file with eleven services, of which four run in production, three run locally and four run in both with different settings.

  compose.yaml           the shared definition. no
                         ports, no volumes, no
                         environment beyond defaults.
  compose.override.yaml   local. bind mounts, exposed
                         ports, mailpit, xdebug.
                         loaded automatically.
  compose.prod.yaml      production. resource limits,
                         restart policies, the log
                         driver, no build sections.

$ docker compose -f compose.yaml -f compose.prod.yaml up -d

The override file being loaded automatically is what makes the split ergonomic locally and is also the trap — a production command that forgets to name its file gets the local overrides silently. The deploy script names both files explicitly and the local one names neither, which is the arrangement that fails safely in the direction that matters.