Route /health, returns ok, everyone happy. That is the first version of every health endpoint on the project we are moving to Kubernetes. Then somebody pastes the same path into livenessProbe and readinessProbe, and a thirty second database hiccup becomes a long evening.

The two probes ask different questions.

Readiness asks: should this pod get traffic right now. Here it is correct to check dependencies. Database unreachable, cache cold, migrations still running: answer no. Kubernetes takes the pod out of the Service, traffic goes to the others, the pod returns when the world improves. Failing readiness is cheap and reversible. A polite “not now”.

Liveness asks: is this process beyond saving. The only honest yes is the process itself being stuck, deadlocked, or broken in a way a restart fixes. Liveness must not check the database. Play it through. Database blinks for thirty seconds, liveness fails on every pod at once, Kubernetes restarts the whole fleet, every PHP-FPM comes up with cold opcache and empty connection pools, all of them hit the recovering database, it goes down again. A restart storm, self-inflicted. A database outage is not a problem a PHP restart can solve, so it has no place in liveness.

For PHP-FPM the honest liveness is almost free: ping.path exists for exactly this. Process answers ping, process is alive. For our long-running workers we write a heartbeat timestamp and let liveness compare it with the clock, because a hung worker still has a listening socket.

And give readiness sane timeouts. Checking the database with a two second timeout while the normal query timeout is thirty means the probe declares death long before the application notices anything.

A restart is a hammer. Liveness decides when to swing it. Be very sure.