Where do the built-in liveness and readiness health groups come from, and how do they connect to Spring's ApplicationAvailability?
answer
- probes.enabled auto-true on Kubernetes
- liveness=LivenessState(CORRECT/BROKEN); readiness=ReadinessState(ACCEPTING/REFUSING)
- ApplicationAvailability bean holds current state
- AvailabilityChangeEvent.publish(ctx, state)
- keep liveness minimal; graceful shutdown flips readiness
basics
~10 sWhen probes are enabled (auto on Kubernetes), Spring Boot auto-creates 'liveness' and 'readiness' groups exposing LivenessStateHealthIndicator and ReadinessStateHealthIndicator. These read the app's LivenessState/ReadinessState via ApplicationAvailability, which you update by publishing AvailabilityChangeEvents.
solid answer
~30 sSetting `management.endpoint.health.probes.enabled=true` (auto-detected when running in Kubernetes) makes Spring Boot register two special health groups: `liveness` and `readiness`, served at `/actuator/health/liveness` and `/actuator/health/readiness`. They wrap `LivenessStateHealthIndicator` and `ReadinessStateHealthIndicator`, which reflect the application's `LivenessState` (CORRECT/BROKEN) and `ReadinessState` (ACCEPTING_TRAFFIC/REFUSING_TRAFFIC) tracked by the `ApplicationAvailability` bean. You change state by publishing an `AvailabilityChangeEvent` — e.g. `AvailabilityChangeEvent.publish(context, ReadinessState.REFUSING_TRAFFIC)` — which flips the readiness probe to DOWN (503) so Kubernetes stops routing traffic, without touching liveness (so the pod isn't killed). Spring also flips readiness automatically during graceful shutdown. You can add your own indicators to these groups via the normal include property.
code
java · 28 linesimport org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
class TrafficGate {
private final ApplicationContext context;
TrafficGate(ApplicationContext context) {
this.context = context;
}
/** Pause traffic: readiness -> OUT_OF_SERVICE (503), pod NOT restarted. */
void stopAcceptingTraffic() {
AvailabilityChangeEvent.publish(context, ReadinessState.REFUSING_TRAFFIC);
}
/** Resume: readiness -> UP (200). */
void resumeTraffic() {
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
}
// application.yml:
// management.endpoint.health.probes.enabled: true
// management.endpoint.health.group.readiness.include: readinessState, dbgo deeper
Know the two probe endpoints exist and roughly what liveness vs readiness mean.
Enable probes and describe the LivenessState/ReadinessState values and their default HTTP codes.
Drive state via AvailabilityChangeEvent and extend readiness with dependency checks appropriately.
Reason about failure-domain design — minimal liveness, cautious readiness dependencies, graceful-shutdown draining, and process-local state — to avoid restart storms and correlated outages.
## Application availability model Spring Boot models runtime availability with two independent state types (`org.springframework.boot.availability`): - **`LivenessState`** — `CORRECT` (the app's internal state is valid; the JVM is running normally) or `BROKEN` (unrecoverable internal failure; the app should be restarted). - **`ReadinessState`** — `ACCEPTING_TRAFFIC` (ready to serve requests) or `REFUSING_TRAFFIC` (alive but temporarily unable/unwilling to serve — e.g. warming caches, shutting down). The current values are held by the `ApplicationAvailability` bean (default impl `ApplicationAvailabilityBean`), which listens to availability events and remembers the latest state of each type. ## Auto-created probe groups `management.endpoint.health.probes.enabled` controls this. It defaults to true **when the app detects a Kubernetes environment** (presence of `KUBERNETES_SERVICE_HOST` env var) or a Cloud Foundry environment; otherwise false. You can force it on anywhere. When enabled, Boot creates: - group **`liveness`** including `LivenessStateHealthIndicator` (id `livenessState`) → `/actuator/health/liveness` - group **`readiness`** including `ReadinessStateHealthIndicator` (id `readinessState`) → `/actuator/health/readiness` `CORRECT`/`ACCEPTING_TRAFFIC` map to `Status.UP` (HTTP 200); `BROKEN`/`REFUSING_TRAFFIC` map to `Status.OUT_OF_SERVICE` — 503 by default. Kubernetes' liveness probe failing → restart; readiness probe failing → removed from Service endpoints. That is exactly the liveness/readiness separation groups were built for. ## Changing state You drive state by publishing an event on the `ApplicationEventPublisher` (or the `ApplicationContext`): ```java AvailabilityChangeEvent.publish(applicationContext, ReadinessState.REFUSING_TRAFFIC); ``` The `ApplicationAvailabilityBean` records it; the readiness health group immediately reports OUT_OF_SERVICE. Publish `ACCEPTING_TRAFFIC` to restore. Spring Boot itself publishes these at lifecycle points — notably, during **graceful shutdown** it sets `REFUSING_TRAFFIC` so the pod drains connections before dying, and it sets `CORRECT`/`ACCEPTING_TRAFFIC` once the context is fully started (`ApplicationStartedEvent` → liveness CORRECT, `ApplicationReadyEvent` → readiness ACCEPTING_TRAFFIC). ## Customizing the groups The probe groups are ordinary health groups, so you can extend them with the include property to fold in dependency checks: ``` management.endpoint.health.group.readiness.include=readinessState, db, redis ``` Now readiness is DOWN if the DB is down too — but be deliberate: making readiness depend on a shared external system can cause correlated pod-wide traffic removal (cascading outage). Liveness especially should stay minimal — ideally only `livenessState` — because anything you add there can trigger mass pod restarts. ## Gotchas & principal-level judgement - **Don't put external dependencies in the liveness group.** A transient DB blip must not restart every pod; that turns a recoverable dependency outage into a restart storm. - Readiness including a shared dependency can pull ALL replicas out of rotation simultaneously — sometimes you want per-instance readiness only. - Probes are OFF by default outside Kubernetes/Cloud Foundry; forgetting to enable them locally means `/actuator/health/liveness` 404s in tests. - The state is process-local; there is no cluster coordination — each pod tracks its own availability. - Combine with `additional-path` and per-group `status.http-mapping` (previous questions) to expose `/livez` and `/readyz` on the main port with the HTTP codes the orchestrator expects. - `@EventListener` on `AvailabilityChangeEvent<ReadinessState>` lets you react to transitions (e.g. log, quiesce work queues).
- Why should you avoid adding an external database check to the liveness group?Liveness failing tells Kubernetes to RESTART the pod. A transient DB outage would then restart every replica repeatedly, converting a recoverable dependency blip into a self-inflicted restart storm. Liveness should reflect only unrecoverable internal state; dependency checks belong in readiness (and even there, cautiously).
- How does Spring Boot behave with these probes during graceful shutdown?It publishes ReadinessState.REFUSING_TRAFFIC, so the readiness group reports OUT_OF_SERVICE (503) and Kubernetes stops sending new traffic while in-flight requests drain, before the context closes.
- Are liveness/readiness groups available by default when running locally, not in Kubernetes?No. management.endpoint.health.probes.enabled defaults to true only in a detected Kubernetes/Cloud Foundry environment; elsewhere you must set it explicitly or the groups 404.
saying these in an interview costs you the question
- Putting external dependency checks in the liveness group
- Thinking probes are enabled by default in every environment
- Believing you must write custom HealthIndicators instead of publishing AvailabilityChangeEvent
- Confusing liveness (restart) semantics with readiness (traffic routing) semantics
- Assuming availability state is cluster-coordinated rather than process-local