How to express startup ordering and runtime dependencies between processes.
| Condition | Meaning | Best for |
|---|---|---|
process_started |
Dependency has spawned (PID exists, may not be ready) | Coarse ordering when readiness doesn't matter |
process_healthy |
Dependency's readiness_probe passes |
Runtime services that must be queryable |
process_completed |
Dependency exited (any code) | One-shot tasks that may fail |
process_completed_successfully |
Dependency exited with code 0 | One-shot init that must succeed |
A common pattern: a web service + a worker daemon that talks to the same DB or queue. Daemon should start AFTER the web app has its DB connection pool warm.
processes:
webapp:
command: "uv run python manage.py serve --port 8000"
working_dir: "X:/Forge/MyApp"
readiness_probe:
http_get: { host: localhost, port: 8000, path: / }
initial_delay_seconds: 10
availability: { restart: always }
worker:
command: "uv run python -m myapp.worker"
working_dir: "X:/Forge/MyApp"
depends_on:
webapp:
condition: process_healthy
availability: { restart: always }
Result: worker doesn't start until webapp's readiness probe passes. If webapp restarts, worker keeps running (depends_on is a startup ordering rule, not a runtime tether).
Web app + background daemon + audit watcher (Axiom pattern):
processes:
app:
command: "..."
readiness_probe: { ... }
app-daemon:
command: "..."
depends_on:
app:
condition: process_healthy
app-feedback:
command: "..."
depends_on:
app:
condition: process_started # weaker — just needs app's pid to exist
Postgres in the same PC stack, app depends on it:
processes:
postgres:
command: "postgres -D /var/lib/pg"
readiness_probe:
tcp_socket: { host: localhost, port: 5432 }
initial_delay_seconds: 3
migrate:
command: "alembic upgrade head"
working_dir: "X:/MyApp"
depends_on:
postgres:
condition: process_healthy
availability:
restart: exit_on_failure # one-shot; if it fails, the whole stack fails
app:
command: "uvicorn main:app"
working_dir: "X:/MyApp"
depends_on:
migrate:
condition: process_completed_successfully
postgres:
condition: process_healthy
availability: { restart: always }
migrate runs once, must succeed. app waits for both migrate (success) and postgres (healthy).
E.g. Cloudflare tunnel exposing a local service:
processes:
mcp-server:
command: "fastmcp serve --port 8000"
readiness_probe:
http_get: { host: localhost, port: 8000, path: / }
initial_delay_seconds: 5
mcp-tunnel:
command: '"C:/Program Files/cloudflared/cloudflared.exe" tunnel run my-tunnel'
depends_on:
mcp-server:
condition: process_healthy # don't open tunnel until server is ready
availability:
restart: always
backoff_seconds: 5
max_restarts: 50 # tunnels can disconnect, allow many retries
processes:
fetch-secrets:
command: "python scripts/fetch_secrets.py"
availability:
restart: exit_on_failure # must complete; stop the project if it fails
# No readiness_probe — task either completes or doesn't
app:
command: "..."
depends_on:
fetch-secrets:
condition: process_completed_successfully
PC detects cycles at startup. This fails immediately:
processes:
a: { depends_on: { b: { condition: process_started } } }
b: { depends_on: { a: { condition: process_started } } }
# Error: dependency cycle detected: a -> b -> a
depends_on Does NOT Dowebapp crashes and recovers, worker doesn't automatically restart.restart: exit_on_failure and probes.--ordered-shutdown flag is used).For runtime coupling, the dependent process needs application-level reconnect/retry logic.
By default PC shuts processes down in any order. For services with stateful deps, use:
process-compose down --ordered-shutdown
# Stops in reverse dependency order: dependents first, then dependencies
probe-patterns.md for crafting good readiness_probes (without these, process_healthy is useless)schema-reference.md for full availability/shutdown field semantics