CD2: Running many containers together

Lesson 3 of 5 · reading 15–25 minutes; practice and project time is on the module page

Compose: the whole app in one file

🎯You'll be able to

Describe several containers, their network, volumes, health checks and start order in a Compose file, and start or stop them together.

🏠The everyday picture

Instead of telling each cook what to make on the day, a wedding caterer writes one menu card listing every dish, who makes it and what must be ready first. Anyone can pick up the card and run the kitchen the same way.

📘What it really is

The kit includes this compose.yaml (shortened). It runs the image tagged routerani:1.0.0, so build that tag first:

services:
  db:
    image: postgres:16.15-alpine
    environment:
      POSTGRES_USER: routerani
      POSTGRES_DB: routerani
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets: [db_password]
    volumes: [db-data:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U routerani -d routerani"]
      interval: 2s
      retries: 15
  app:
    image: routerani:1.0.0
    environment:
      APP_VERSION: 1.0.0
      DATABASE_URL: postgres://routerani:${DB_PASSWORD}@db:5432/routerani
    depends_on:
      db: { condition: service_healthy }
    deploy: { replicas: 2 }
  lb:
    image: nginx:1.30.6-alpine
    volumes: [./infra/nginx.conf:/etc/nginx/nginx.conf:ro]
    ports: ["127.0.0.1:8081:80"]
    depends_on: [app]
volumes:
  db-data:
secrets:
  db_password:
    environment: DB_PASSWORD

📝A worked example

Input: in the kit, docker build -t routerani:1.0.0 app, export DB_PASSWORD=$(openssl rand -hex 12), then docker compose up -d, then docker compose ps and six requests to http://localhost:8081/version.

Expected output: db, two app copies (both healthy once your image has the health check from CD1) and lb running; the x-served-by header alternates between two container IDs. After a booking, docker compose down then up -d keeps the booking; docker compose down -v deletes it. Tested on 1 November 2026 with Docker 29.9.2 and Compose 5.6.1 on Linux.

🌍Where it's used

📝Case scenario

A new developer at a logistics company spent three days installing the right PostgreSQL, Redis and Elasticsearch versions from a wiki page that was out of date. The team wrote a Compose file with pinned versions and health checks. The next new joiner was running tests within an hour.

🖱️Try it

Start the stack, then run docker compose stop db and request /ready through the load balancer. Start it again with docker compose start db. Success: /ready returns 503 while the database is stopped and 200 within a few seconds of it returning.

⚠️Common mistakes

🛠️For your project

The projects use Terraform rather than Compose, but every idea here (network, aliases, volume, health checks) appears again in infra/main.tf.

✅Quick check

What does condition: service_healthy add to depends_on?

Show the answer

It waits until the dependency's health check passes, not just until its container starts.

What does docker compose down keep?

Show the answer

The volumes, unless you add -v.

← PreviousVolumes: keeping data when containers go
ProLearners Assist