Kubernetes Probes: How to Use Liveness, Readiness, and Startup Probes Correctly
by Lord_evron
Kubernetes probes look like three simple fields, but treating them all as a generic “restart the pod if anything feels slightly off” panic button is a classic trap. They actually have very different jobs, and using them interchangeably is a quick way to turn a minor hiccup into an endless loop of unnecessary restarts. Let’s learn to use them properly!
The three probes answer three different questions, and each one triggers a different action:
| Probe | Question it answers | What a failure does |
|---|---|---|
| Liveness | Is this container stuck in a way a restart could fix? | Restart the container |
| Readiness | Can this Pod serve traffic right now? | Remove the Pod from the Service |
| Startup | Has the application finished initializing? | Keep waiting, hold off the others |
That table is the whole article, really. The rest is about why mixing the three up is expensive: a badly designed liveness probe can turn a two-second network blip into a full outage.
Where Probes Are Defined
Probes are not part of the Service. They are defined on the container, inside the Pod template — so in practice in a Deployment, StatefulSet, or DaemonSet:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: my-api:1.0
livenessProbe:
httpGet:
path: /health/live
port: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
The Service never mentions probes at all. It simply routes traffic to whichever Pods are currently Ready:
Kubernetes Service
|
v
Ready Pods receive traffic
|
+------------+------------+
| | |
Pod #1 Pod #2 Pod #3
| | |
readiness readiness readiness
probe probe probe
When a readiness probe fails, Kubernetes drops that Pod from the Service’s set of ready endpoints. Traffic stops, but the container keeps running. That separation between stop the traffic and restart the container is the entire reason there is more than one probe.
The Three Probes
Startup — “have I finished starting?”
Some applications simply take a long time to come up: loading configuration, warming caches, running migrations, loading large models. If liveness starts checking immediately, Kubernetes may kill the container before it ever had a chance to startup!
startupProbe:
httpGet:
path: /health/startup
port: 8080
periodSeconds: 5
failureThreshold: 30
While a startup probe is defined and still failing, the liveness and readiness probes are disabled entirely. Here the application gets up to 150 seconds (5 × 30) to initialize, and can finish much sooner if it is fast. Once the startup probe succeeds it is never run again, and the other two take over. This is far better than guessing a large initialDelaySeconds, which forces you to pay the worst case every single time.
Liveness — “should Kubernetes restart me?”
A liveness probe is for a container that is alive at the process level but no longer doing useful work: a deadlock, an exhausted thread pool, a wedged event loop. The process still exists, so without a probe Kubernetes would consider it perfectly healthy forever.
livenessProbe:
httpGet:
path: /health/live
port: 8080
periodSeconds: 10
failureThreshold: 3
With these values the container is restarted only after roughly thirty seconds of consistent failure. That word matters: liveness should describe the application’s own ability to function, not the health of everything it happens to talk to.
Readiness — “should I receive traffic?”
Readiness answers a narrower question: this Pod is running, but should it be serving requests at this moment?
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 5
failureThreshold: 2
Readiness failure is cheap and fully reversible. The Pod stays alive, drops out of the Service endpoints, and returns as soon as the probe succeeds again — no restart, no lost in-memory state, no startup cost paid. This is why readiness, not liveness, is the right place for “I am temporarily unable to do my job”.
The Database Trap
This is the mistake worth internalizing, because it is both extremely common and actively harmful.
Imagine an API with three replicas, all talking to the same database, and a /health endpoint that checks database connectivity wired into the liveness probe. The database has a brief network problem, a few seconds of network issue, the kind of thing that happens routinely.
All three replicas share that dependency, so they all fail at the same time:
DB hiccup
|
+------> Pod 1 liveness fails
|
+------> Pod 2 liveness fails
|
+------> Pod 3 liveness fails
|
v
Entire fleet restarts
|
v
Cold caches, reconnect storm
|
v
More pressure on the recovering DB
By the time the database is back, every Pod is in the middle of a cold start that can take few minutes depending from the application, and they all hit the recovering database at once. The miss-configured probes has taken a transient dependency failure and amplified it into a real outage.
The rule that prevents this is a single question:
Liveness asks whether restarting this container could plausibly fix the problem.
Restarting an API Pod does not repair a database. So database connectivity has no business being in a liveness probe. It belongs in readiness, where the consequence matches the problem:
Database unavailable
|
+----> Readiness fails
| |
| v
| Pod removed from Service
| (still running)
|
+----> Liveness still healthy
|
v
No restart
The Pods stay up, stop advertising themselves as able to serve, and become Ready again on their own the moment the database recovers. Same failure, but back online in seconds with no restarts!
How Much Should Readiness Check?
The opposite mistake also exists. Readiness is not a dumping ground for every dependency either.
If your API genuinely cannot answer anything useful without the database, then checking it in readiness is correct. But suppose the application can still serve cached data, /metrics, and a handful of read paths when Redis is down. Making readiness fail on Redis pulls every Pod out of the Service — and since all replicas share that Redis, you have just taken the whole application offline over a dependency it could have tolerated.
The question to ask is:
Can this Pod still serve the traffic the Service is actually sending it?
If the answer is yes, it should stay Ready, even in a degraded state.
Designing Health Endpoints
The cleanest approach is one endpoint per question, rather than a single /health doing everything:
/health/live— lightweight, and limited to the application itself. It should generally avoid touching databases, queues, or remote APIs unless there is a very specific reason./health/ready— may check the dependencies that are genuinely required to serve requests, returning503when they are missing./health/startup— reports only whether initialization has completed.
Whatever they check, these endpoints must be cheap. With 100 Pods and a five-second period, you are generating tens of thousands of health checks per hour, so they need to be fast, deterministic, side-effect free, and safe to call repeatedly. A readiness check that fans out to six backends is not a health check, it is a distributed tracing span you run twelve times a minute per Pod — and when one of those backends gets slow, the probe gets slow, Pods drop out of the Service, and the remaining Pods absorb more traffic. The probe itself becomes the outage.
Choosing the Right Probe
When you are unsure where a particular check belongs, don’t ask whether it indicates a problem. Ask what you want Kubernetes to do about it:
- “Restart the container, because restarting might fix it” → liveness
- “Stop sending traffic here until this resolves” → readiness
- “Give it more time to finish starting” → startup
This works because probes are not monitoring endpoints. Monitoring tells a human that something is wrong; a probe tells Kubernetes what action to take, and Kubernetes takes it immediately, on every replica, without asking. If you want to observe something, export a metric and alert on it. Put it in a probe only when you want the corresponding action.
What to remember
Choosing the right probe is extremely important, because that gives Kubernetes the right signal for the action you actually want. A temporary database outage should never be a reason to restart every API Pod.
So to conclude, liveness is a big hammer: it just restarts pods, so reserve it for problems a restart can genuinely fix. Readiness is the gentle, reversible one, and it is where most dependency conditions belong. Startup exists so your container has enough time to start, before the other two take over.
In the next article we will look at probe types, the timing parameters, and how to tune all of this for real production workloads.
tags: secops - technology - linux