Skip to content

ADR-004: Dependency-Free Liveness

Status: Accepted

Context

Kubernetes distinguishes liveness (restart the pod if it fails) from readiness (remove the pod from the Service if it fails). A widespread anti-pattern is pointing the liveness probe at a full health check: when a shared dependency (database, broker) blinks, every pod fails liveness simultaneously and the orchestrator restarts perfectly healthy processes in a loop — a self-inflicted outage on top of the dependency incident.

Decision

GET /actuator/health/live returns {"status": "UP"} unconditionally. It proves the process answers HTTP — the event loop is alive — and nothing else. Dependency health is served by /actuator/health/ready (aggregate of all indicators) and /actuator/health (aggregate + detail).

Consequences

Positive:

  • Restart storms during dependency outages are impossible by construction.
  • The endpoint is allocation-free and safe to poll at high frequency.
  • The liveness/readiness split maps one-to-one onto Kubernetes probe semantics (documented in the how-to guide).

Negative:

  • A deadlocked worker thread can still answer liveness (the event loop responds); detecting internal starvation would need a dedicated indicator on readiness or an external watchdog.
  • Operators expecting Spring Boot's configurable liveness-state machine (LivenessState.BROKEN) will find no equivalent — by design, for now.

Alternatives Considered

  • Liveness = aggregate of all indicators: rejected — couples pod restarts to dependency outages (the anti-pattern above).
  • Configurable indicator subset for liveness: rejected for v0.x — YAGNI until a real deployment needs it; readiness already covers dependency gating.