Stop Copy-Pasting Parts in Docker Compose
Imagine you have a complex microservice. For local development, you need it connected to a message queue, a telemetry collector, and a database. But for your E2E testing, you need a slightly modified version (different env vars, an extra mock dependency, maybe a different port). Most engineers solve this by maintaining two massive, almost-identical YAML files. It is a nightmare to sync changes. Or they simply do this: $ docker compose -f compose.yml -f e2e-compose.yml up --build -d I do NOT like neither of them. Instead use YAML Anchors (&) , Merge Keys (<<:) , and Compose Profiles to create a single, DRY (Don't Repeat Yourself) configuration file. # ------------------------------------------------------------ # 1. REUSABLE BUILDING BLOCKS (Anchors) # ------------------------------------------------------------ x-backend-depends-on : &backend-depends-on message-queue : condition : service_healthy telemetry-collector : condition : service_started x-backend-config : &backend-config build : . user : " 1000:1000" ports : - " 3000:$PORT" env_file : - .env healthcheck : test : [ " CMD" , " curl" , " -f" , " http://localhost:${PORT:-3000}/health" ] interval : 5s timeout : 5s retries : 12 start_period : 10s depends_on : *app-depends-on # ------------------------------------------------------------ # 2. SERVICES # ------------------------------------------------------------ services : # --- Production / Dev Service --- backend : << : *backend-config profiles : [ " dev" ] # --- E2E Test Variant --- backend-e2e : << : *app-config profiles : [ " e2e" ] # Only starts when explicitly called environment : # Override specific ENV vars for testing RETRY_DELAY_MS : " 100" TIMEOUT_MS : " 200" depends_on : << : *app-depends-on # Inherit all base dependencies e2e-fixture : # ADD an extra dependency for testing condition : service_healthy # ... So now how this changes your workflow: Local: docker compose --profile dev up starts only backend + services in default/dev profile. E2E testing: