DEV Community

Cover image for Stop Copy-Pasting Parts in Docker Compose

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
Enter fullscreen mode Exit fullscreen mode

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
  # ...
Enter fullscreen mode Exit fullscreen mode

So now how this changes your workflow:

  • Local: docker compose --profile dev up starts only backend + services in default/dev profile.
  • E2E testing: docker compose --profile e2e up automatically swaps in backend-e2e, overrides timeouts/retries.

Top comments (0)