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.
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
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.
See what actually stuck.
Take the practice scenarios now.