# Liveness and readiness checks

> One probe asks “should I restart you?”, the other “should I send you traffic?” — mixing them up causes outages.

Concept · Web services · https://levelcode.org/concepts/liveness-readiness

Orchestrators and load balancers keep asking your service whether it's healthy. Kubernetes asks two different questions, and they need different answers.

| Probe | Question | On failure |
|---|---|---|
| **Liveness** | Is this process stuck beyond repair? | The container is **restarted** |
| **Readiness** | Can this instance handle a request right now? | It's **removed from the Service's endpoints**; no restart |
| Startup | Has it finished booting? | Liveness and readiness wait until it passes |

```yaml
livenessProbe:
  httpGet: { path: /livez, port: 8080 }
  periodSeconds: 10
  failureThreshold: 3
readinessProbe:
  httpGet: { path: /readyz, port: 8080 }
  periodSeconds: 5
```

## What each endpoint should check

**Liveness: almost nothing.** Return 200 if the process can serve HTTP at all. Never check the database here. If the database goes down, every pod fails liveness, every pod restarts, and a database outage becomes a restart loop across the whole fleet that still can't fix the database.

**Readiness: what this instance needs to do useful work.** Has it loaded its config and warmed its caches? Can it reach the dependencies it can't work without? Is it shutting down? Fail readiness during [graceful shutdown](https://levelcode.org/concepts/graceful-shutdown) so traffic drains away before the process exits.

```go
mux.HandleFunc("GET /livez", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
mux.HandleFunc("GET /readyz", func(w http.ResponseWriter, r *http.Request) {
	if shuttingDown.Load() || !warm.Load() {
		http.Error(w, "not ready", http.StatusServiceUnavailable)
		return
	}
	w.WriteHeader(http.StatusOK)
})
```

## Pitfalls

- **Shared dependencies in readiness.** If every pod checks the same database and it blips, every pod goes unready at once and the service serves nothing, not even cached or degraded responses. Check only what's specific to the instance, or use a slower threshold for shared dependencies.
- **Probes that do real work** (run a query, call another service) add load and can time out under exactly the pressure you're trying to detect. Keep them cheap.
- **Timeouts shorter than a GC pause or cold start** cause restarts that look random. Use a startup probe for slow boots.

Load balancers outside Kubernetes run the same readiness-style check to decide whether to send traffic to a target. See [API gateway vs load balancer](https://levelcode.org/concepts/api-gateway-vs-load-balancer). Health checks are also the signal that [rolling and canary deploys](https://levelcode.org/concepts/deployment-strategies) use to decide whether to continue.
