fivenines
35/40
Lesson 35 · Orchestration

Dependency Ordering

In one sentence: depends_on declarations form a DAG; the engine starts services in topological order, in parallel where the graph allows — and only a healthy-gated dependency actually waits for readiness rather than mere startup.

You already know

  • From Lesson 24: Running ≠ working — health checks exist precisely for this gap.
  • From Lesson 34: a compose file declares all services up front, so the graph is known before anything starts.

Topological start, maximal parallelism

Dependencies must form a DAG (a cycle is a config error, rejected at parse time). The engine repeatedly starts every service whose dependencies are all satisfied — meaning independent services launch simultaneously. But "satisfied" has two strengths, and the difference is where real outages hide. started (the default) waits only for the container to exist and run; a db that's "started" may still be replaying its journal and refusing connections. service_healthy waits for the dependency's health check (Lesson 24) to report Healthy — genuine readiness. The rule of thumb: gate on health wherever the depender's first action is to connect.

Architecture — a dependency DAG with parallel waves
flowchart TB
  DB["db
(healthcheck: accepts connections)"] RD["redis"] MIG["migrate
depends_on db: service_healthy"] API["api
depends_on: migrate complete, redis started"] WEB["web
depends_on api: service_healthy"] DB --> MIG MIG --> API RD --> API API --> WEB W1["wave 1 (parallel): db, redis"] W2["wave 2: migrate"] W3["wave 3: api"] W4["wave 4: web"] style W1 fill:#eaf1fe,stroke:#1d63ed style W2 fill:#eaf1fe,stroke:#1d63ed style W3 fill:#eaf1fe,stroke:#1d63ed style W4 fill:#eaf1fe,stroke:#1d63ed
Sequence — health-gated startup doing its job
sequenceDiagram
  participant C as Compose engine
  participant DB as db
  participant M as migrate
  C->>DB: start (wave 1)
  DB->>DB: Running — but replaying journal, refusing connections
  C->>C: migrate waits (gate: service_healthy, not started)
  DB->>DB: journal done → health probe passes → Healthy
  DB-->>C: health event: Healthy
  C->>M: start migrate (its first act: connect to db — succeeds)
  note over C: with the default "started" gate,
migrate would have connected into the journal replay and died

Anchor these

  • Dependencies form a DAG; starts proceed in parallel topological waves.
  • "started" ≈ the process exists; "service_healthy" = the service answers. Gate connections on health.
  • Cycles are config errors — reject at parse, not at 3 a.m.
Next step

See what actually stuck.

Take the practice scenarios now.