background-shape
Beyond docker-compose up, Real-World Patterns in 2022
July 1, 2022 · 4 min read · by Muhammad Amal programming

TL;DR — Compose v2.6 (Go binary, ships with current Docker) added watch + better profiles + saner depends_on. Use profiles for opt-in services, healthchecks + service_healthy for boot ordering, secrets for credentials, watch for live reload. The 50-line compose file is the goal.

After six months covering containerization, microservices, and Clean Architecture, July goes deep on Docker Compose. We’ve used it casually since January’s setup post. This month is everything I wished I’d known then.

Compose v2 (the Go rewrite, shipped with Docker Desktop since late 2021) is what most teams are running in 2022. The patterns here assume v2.6+.

What changed since v1

If you bounced off Compose around the v1.x era, the headline changes:

  • One binarydocker compose (no hyphen). Same as Docker CLI.
  • Faster — Go reimplementation, no Python overhead.
  • Profiles — opt-in services without copy-pasted compose files.
  • condition: service_healthy in depends_on — actual wait-for-it.
  • watch — file-watching live reload (v2.6+).
  • Better secretssecrets: top-level + mounts as files.
  • No version: field needed — schema is implicit.

The shape of a 2022 compose file looks different from a 2018 one. Less ceremony, more real config.

The compose file I keep coming back to

Anchoring the rest of July with the file I’d hand a new engineer:

name: my-stack

services:
  postgres:
    image: postgres:14-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7-alpine
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10

  api:
    build:
      context: .
      target: dev
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    env_file: .env.local
    ports:
      - "8080:8080"
    volumes:
      - ./:/app
      - /app/node_modules

  kafka:
    image: bitnami/kafka:3.2
    profiles: ["events"]
    # ... config

  mailhog:
    image: mailhog/mailhog
    profiles: ["mail"]
    ports:
      - "1025:1025"
      - "8025:8025"

volumes:
  postgres-data:
  redis-data:

50 lines. Bring up: docker compose up -d. Add Kafka: docker compose --profile events up -d.

Six things worth highlighting:

name: — sets the project name explicitly. Without it, project name comes from directory name, which differs between contributors.

condition: service_healthyapi waits for Postgres + Redis to be actually responding, not just running. Eliminates the “first run fails because DB wasn’t ready” race.

Anonymous volume /app/node_modules — the bind mount ./:/app would otherwise clobber node_modules from the host (which is empty or wrong). Anonymous volume “shadows” the bind for that path.

build: target: dev — same Dockerfile, different stage. Production builds use a later stage; dev uses one optimized for fast rebuilds.

Profiles for heavyweight services — Kafka and MailHog don’t run by default. Opt in when needed.

No version: field — Compose v2 doesn’t require it. Older docs still show version: "3.8"; drop it.

Commands worth knowing past up

# Bring up in background, follow specific logs
docker compose up -d
docker compose logs -f api

# Run a one-off command
docker compose exec api npm test
docker compose exec postgres psql -U app

# Restart just one service after a config change
docker compose restart api

# Rebuild a service's image
docker compose build api
docker compose up -d api

# Tear down, keep data
docker compose down

# Nuke everything including volumes
docker compose down -v

# Show running stacks across all directories
docker compose ls

# Run with profile
docker compose --profile events up -d

# Run multiple profiles
docker compose --profile events --profile mail up -d

The compose ls is new in v2 and underused. Tells you all stacks running across all directories on your machine. Useful when you start three projects and forget which Postgres is whose.

What this month covers

Twelve more posts:

Common Pitfalls

Treating compose as production. It’s local dev + CI + ephemeral envs. Not k8s replacement.

Mixing v1 and v2 docs. Lots of internet content is v1. If you see docker-compose (hyphen) and version: "3", it’s pre-2021.

Override files growing endlessly. docker-compose.override.yml is fine for one local override; past three layers you’re rebuilding kustomize.

Treating volumes like bind mounts. Different behavior, different use cases. Covered Jul 15.

Wrapping Up

Compose v2.6 is the foundation for local dev in 2022. The patterns this month make it scale past simple single-service setups. Monday: polyglot stacks — Compose files for repos with Go services + Node frontend + Python ETL.