| Probe | Question it answers | Failure action | Endpoint here |
|---|---|---|---|
| startup | Has the app finished booting? | Kill & restart the container (only after the budget is exhausted) | /actuator/health/liveness |
| liveness | Is the app wedged and unrecoverable? | Kill & restart the container | /actuator/health/liveness |
| readiness | Can the app serve traffic right now? | Remove pod from Service endpoints (no restart) | /actuator/health/readiness |
Key rule: only liveness and startup restart the container. Readiness never does — it only gates traffic.
Answers "can this pod serve requests?" When it fails, Kubernetes removes the pod from the Service's endpoints, so traffic is withheld until it passes again. It does not restart anything.
Because failing readiness is cheap (no restart), it is legitimate — often desirable — to include external dependencies here (e.g. the database), so a pod stops receiving traffic when it can't actually fulfil requests.
Answers "is the app broken beyond recovery and in need of a restart?" When it
fails failureThreshold times, Kubernetes SIGKILLs and restarts the
container (you'll see exit code 137).
A restart only helps for internal faults (deadlock, broken application context). It does not fix an external dependency outage. Therefore liveness must depend only on internal app state, never on the DB or downstream services — otherwise a transient dependency blip makes every pod fail liveness at once and restart in a storm that can't fix the problem.
Answers "has the app finished starting up?" It exists to protect a
slow-booting container from the liveness probe. While the startup probe is
still failing, liveness and readiness are suspended; the container gets up
to failureThreshold × periodSeconds to start. The first time the startup
probe succeeds, Kubernetes stops running it and hands off to liveness +
readiness. If it never succeeds within the budget, the container is killed.
Spring Boot Actuator exposes:
/actuator/health— aggregated: rolls up every indicator (db,diskSpace, downstream clients, custom checks, plus liveness/readiness state). Any one indicatorDOWNmakes the whole endpointDOWN./actuator/health/liveness— the liveness group; by default onlylivenessState(internal "is the context alive" signal). No DB./actuator/health/readiness— the readiness group;readinessState, and may include dependency checks likedb.
Requires
management.endpoint.health.probes.enabled=true, which Spring Boot auto-enables when it detects it's running on Kubernetes.
Pointing startup at aggregated /actuator/health relocates the same
anti-pattern to boot time: if the DB is slow or unavailable during startup, the
aggregated endpoint never reports UP, the startup budget is exhausted, and
Kubernetes kills a container whose application actually started fine. The
startup probe should mirror liveness (same livenessState check, longer
budget) so "startup is done" means "the app is up", and dependency availability
is delegated to readiness (where failing only withholds traffic).
| Probe | Recommended endpoint | Why |
|---|---|---|
| startup | /actuator/health/liveness |
Mirrors liveness; "app booted", not "deps reachable" |
| liveness | /actuator/health/liveness |
Restart only on internal fault, never on dependency blips |
| readiness | /actuator/health/readiness |
Safe to include DB/deps — failing just stops traffic |
helm/templates/deployment.yaml:
startupProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
failureThreshold: 12 # 12 × 5s = ~60s startup budget
periodSeconds: 5
readinessProbe:
httpGet:
port: 8080
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
initialDelaySeconds: 10