Profiles for Optional Services

Some services belong in the file without starting on every up, such as a test runner or a database browser. A service with a profiles: list starts only when one of its profiles is active; services without the key always start. BookNest's test runner, built from the Dockerfile's test stage (Multi-Stage Dockerfile), needs the API's PG* variables, so they move into an extension field, a top-level x- key that Compose 514 ignores, and a YAML anchor (&pg-env) lets both reuse it by alias, as environment: *pg-env in api:

compose.yaml: shared variables and an optional test serviceYAML
x-pg-env: &pg-env
  PGHOST: db
  PGUSER: ${POSTGRES_USER:-booknest}
  PGPASSWORD: ${POSTGRES_PASSWORD:-booknest}
  PGDATABASE: ${POSTGRES_DB:-booknest}
services:
  test:
    build:
      context: .
      target: test
    profiles: [test]
    environment: *pg-env
    depends_on:
      db:
        condition: service_healthy

Activate a profile with --profile test or COMPOSE_PROFILES=test; naming a profiled service on the command line activates it too, which is how a CI job runs the suite:

The default services, and a one-off test runShell
docker compose config --services
docker compose run --rm test 2>&1 | grep -E '^ℹ (tests|pass|fail)'
Output
db
api
ℹ tests 9
ℹ pass 9
ℹ fail 0

A plain up still starts only db and api. run --rm test built the test stage, waited for a healthy database, ran the nine tests and removed its container, returning the suite's exit code for CI. A tools profile with a database browser such as adminer:5 works the same way.