All explainers

Liveness vs readiness: one restarts, one just stops traffic

2 min+15 XP

Readiness asks "should I send you work?". Liveness asks "should I put you out of your misery?". Wiring the second one to a slow dependency is how a brief blip becomes a full outage.

Both probes hit an endpoint on your pod, on a schedule, and both mark it failed after enough misses. What differs is entirely what Kubernetes does about it.

One pod, one slow database, two probe configurations

1 / 4

readinessProbe

TrafficPodService1 endpointPodReadyDatabase12mstraffic

Probe passing. The pod is in the Service's endpoints.

livenessProbe

TrafficPodService1 endpointPodRunningDatabase12mstraffic

Identical starting point. Probe passing, traffic flowing.

A healthy pod. The probe passes, the Service has it in its endpoint list, and traffic flows.

Why this specific mistake is so common

The two probes take the same fields, so the natural thing is to copy the block and change one word:

livenessProbe:
  httpGet: { path: /healthz, port: 8080 }
readinessProbe:
  httpGet: { path: /healthz, port: 8080 }   # ← same endpoint

That looks tidy and is quietly dangerous. If /healthz checks the database, you have told Kubernetes "restart me whenever the database is slow", and since every replica shares that database, every replica restarts together.

The endpoints should ask different questions:

  • /healthz (liveness): "is this process wedged?" Return 200 unless the process is genuinely unrecoverable: a deadlocked thread pool, a corrupted in-memory state. No dependency calls. If in doubt, return 200.
  • /ready (readiness): "can I serve a request right now?" Here you check the database, the cache, the migration state. Failing is cheap and reversible.
 readinessProbelivenessProbe
On failurePod removed from the Service's endpoints.Container killed and restarted.
Reversible?Yes, passes again, traffic returns instantly.No. A restart is a restart.
Cost of a false positiveA little lost capacity.Cold start, dropped requests, possible crash loop.
Should it check dependencies?Yes, that is the point.No. Only the process's own health.
Blast radiusOne pod at a time.Every replica at once, if they share the cause.
Safe defaultCheck what you need to serve.Return 200 unless you are certain.

The startup case

There is a third probe that exists purely because people used to solve slow boots by loosening liveness. startupProbe runs first, and both other probes are disabled until it passes:

startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
  periodSeconds: 10        # up to 5 minutes to boot

That buys a slow-starting app five minutes without giving it a permanently lax liveness threshold for the rest of its life.

Remember this

  1. 1Readiness controls traffic. Liveness controls restarts. Every other difference follows from that one.
  2. 2Never check a shared dependency in a liveness probe: every replica shares it, so every replica restarts at once.
  3. 3Liveness is for a wedged process, not a slow one. If you are unsure whether to fail it, return 200.

Now try these questions