level 08 / docker-compose

Docker Compose for Test Stacks

Orchestrate multi-service test environments with Docker Compose — app, database, and test runner together.

Test Stack Architecture

Basic docker-compose.yml

# docker-compose.yml
version: '3.9'

services:
  app:
    image: my-app:latest
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://user:pass@db:5432/testdb
      REDIS_URL: redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 5s
      timeout: 3s
      retries: 10

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: testdb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d testdb"]
      interval: 3s
      timeout: 3s
      retries: 15

  redis:
    image: redis:7-alpine

  pw-tests:
    image: mcr.microsoft.com/playwright:v1.45.0-jammy
    working_dir: /app
    volumes:
      - .:/app
      - playwright-report:/app/playwright-report
    environment:
      BASE_URL: http://app:3000
      DATABASE_URL: postgres://user:pass@db:5432/testdb
      CI: "true"
    command: npx playwright test
    depends_on:
      app:
        condition: service_healthy

volumes:
  playwright-report:

Running the Stack

# Start all services and run tests
docker compose up --exit-code-from pw-tests

# Exit code matches the test runner exit code:
# 0 = all tests passed
# 1 = test failures
# Use for CI pass/fail

# Teardown (remove containers + networks)
docker compose down

# With volume cleanup
docker compose down -v

Override File for CI vs Local

# docker-compose.override.yml (local dev — not committed or committed separately)
services:
  pw-tests:
    command: npx playwright test --headed
    environment:
      PWDEBUG: "1"
# docker-compose.ci.yml (CI-specific overrides)
services:
  pw-tests:
    command: npx playwright test --reporter=blob,junit
# CI usage
docker compose -f docker-compose.yml -f docker-compose.ci.yml up --exit-code-from pw-tests

Healthchecks — Critical for Test Reliability

Without healthchecks, the test container starts before the app is ready:

app:
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
    interval: 5s
    retries: 10

pw-tests:
  depends_on:
    app:
      condition: service_healthy    # wait until app healthcheck passes

Database Seeding

db-seed:
  image: postgres:16-alpine
  environment:
    PGPASSWORD: pass
  command: >
    sh -c "
      until pg_isready -h db -U user; do sleep 1; done
      psql -h db -U user -d testdb -f /seeds/seed.sql
    "
  volumes:
    - ./seeds:/seeds
  depends_on:
    db:
      condition: service_healthy

Useful Commands

# Watch test logs
docker compose logs -f pw-tests

# Run specific test file
docker compose run --rm pw-tests npx playwright test e2e/auth.spec.ts

# Open interactive shell in test container
docker compose run --rm pw-tests bash

# Scale workers
docker compose up --scale pw-tests=4  # 4 test containers (shard-like)