Probes: Liveness, Readiness and Startup
Three probes, three different jobs. Why a liveness probe that checks the database can take down your whole fleet, and what startup probes actually fix.
Three Probes, Three Questions
Probes are where a large share of production incidents are created — not because they're complex, but because the three are easy to conflate. Each answers a different question and each has a different consequence when it fails.
| Probe | Question | On failure |
|---|---|---|
| Startup | Has it finished booting? | Keep waiting; liveness is suspended |
| Readiness | Can it serve traffic right now? | Removed from Service endpoints — not restarted |
| Liveness | Is it unrecoverably stuck? | Container is restarted |
The distinction to hold onto: readiness removes traffic, liveness kills the container. Choosing the wrong one turns a recoverable blip into an outage.
Readiness: Controlling Traffic
A readiness probe decides whether a pod appears in its Service's endpoints. Fail it and traffic stops arriving — but the container keeps running and can recover.
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
failureThreshold: 3This is the right response to temporary unavailability: a full connection pool, a warming cache, a dependency that's briefly unreachable. The pod steps out of rotation, recovers, and steps back in — with no restart and no lost in-memory state.
Readiness is also what makes rolling updates safe. From the previous guide: without a readiness probe, a pod counts as available the moment it starts, so Kubernetes retires an old pod and replaces it with one that can't serve yet. Readiness is the difference between a zero-downtime rollout and a rollout that drops requests.
Liveness: the One That Can Hurt You
A liveness probe restarts the container when it fails. That makes it useful for exactly one thing: a process that is alive but permanently stuck — a deadlock, an event loop wedged forever.
It is dangerous precisely because restarting is a big hammer.
livenessProbe:
httpGet:
path: /healthz # must NOT check external dependencies
port: 8080
periodSeconds: 10
failureThreshold: 3Never check external dependencies in a liveness probe. If /healthz verifies the database and the database has a brief hiccup, every replica fails liveness simultaneously and Kubernetes restarts your entire fleet — at the exact moment the database was already struggling. Restarting your application does nothing to fix a database, and now you have a thundering herd of cold pods reconnecting at once.
Dependency checks belong in readiness, where the consequence is "stop sending me traffic" rather than "destroy me".
A liveness probe should answer only: is this process still capable of making progress on its own? Usually that means a trivial endpoint that returns 200 if the event loop is responsive, touching nothing external.
It is entirely reasonable to have no liveness probe at all. If your application crashes on unrecoverable errors, the container exits and Kubernetes restarts it anyway. A liveness probe adds value only when your process can hang without exiting. Many teams would be better off with readiness probes alone.
Startup: Protecting Slow Boots
The problem startup probes solve: a JVM or heavyweight framework takes 60 seconds to boot. Your liveness probe checks every 10 seconds with a threshold of 3 — so at 30 seconds it declares the container dead and restarts it. It never finishes starting. You get CrashLoopBackOff for an application that has nothing wrong with it.
The old workaround was a large initialDelaySeconds on the liveness probe — which forces you to trade slow failure detection for slow startup tolerance.
A startup probe separates the two concerns:
startupProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 5
failureThreshold: 30 # allows up to 150s to boot
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
failureThreshold: 3 # once started, detects a hang in ~30sWhile the startup probe is running, the liveness probe is disabled entirely. Once it passes once, liveness takes over with its tight timings. Slow boot tolerated, fast failure detection retained.
Check yourself
A team's liveness probe hits /health, which verifies database connectivity. The database fails over for 20 seconds. What happens to the 12 application replicas?
Probe Types and Timing
Four handlers are available:
httpGet: { path: /ready, port: 8080 } # 2xx or 3xx = success
tcpSocket: { port: 5432 } # can the port be connected to?
exec: { command: ["sh","-c","pg_isready"] } # exit 0 = success
grpc: { port: 9090 } # gRPC health checking protocoltcpSocket is weak — a port being open says little about whether the application works. Prefer httpGet or grpc where you can.
The timing fields combine into a real budget worth calculating:
| Field | Default | Meaning |
|---|---|---|
initialDelaySeconds | 0 | Wait before the first probe |
periodSeconds | 10 | Interval between probes |
timeoutSeconds | 1 | A probe slower than this counts as failed |
failureThreshold | 3 | Consecutive failures before acting |
successThreshold | 1 | Consecutive successes to recover |
Detection time is roughly periodSeconds × failureThreshold. With defaults that's ~30 seconds before a liveness restart.
timeoutSeconds defaults to 1 second, which is aggressive. An endpoint that occasionally takes 1.2 seconds under load registers as a failure — so your probe starts failing precisely when the system is busiest. If your health endpoint does any real work, raise it.
Reading Probe Failures
kubectl describe pod api-7d4f8b9c5-x2kfpThe events show exactly which probe failed and why:
Warning Unhealthy 2m (x3 over 2m) kubelet Readiness probe failed: HTTP probe failed with statuscode: 503
Warning Unhealthy 1m (x3 over 1m) kubelet Liveness probe failed: Get "http://10.1.2.3:8080/healthz": context deadline exceeded
Normal Killing 1m kubelet Container api failed liveness probe, will be restarted
That third line is the one to look for when a pod is restarting and you can't work out why. context deadline exceeded means the probe timed out — often a timeoutSeconds that's too tight rather than a genuinely broken application.
A pod showing READY 0/1 while STATUS says Running is a readiness failure, and it's receiving no traffic. That's the state to look for when a Service has no endpoints — the pods are there and running, they're just not ready.
A Sensible Default Configuration
startupProbe:
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 5
failureThreshold: 30 # generous boot budget
readinessProbe:
httpGet: { path: /ready, port: 8080 } # checks dependencies
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
livenessProbe:
httpGet: { path: /healthz, port: 8080 } # checks NOTHING external
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3Two endpoints doing two different jobs: /healthz proves the process is responsive, /ready proves it can actually serve a request including its dependencies.
Check yourself
Which single change most improves a rolling update that currently drops requests?
What's Next
Your workloads run and receive traffic at the right moment. The next guide covers their configuration — and the ConfigMap behaviour that makes a config change appear to do absolutely nothing.