skip to content

How are the liveness and readiness health groups composed, and how would you add your own health checks or a custom availability dimension into a probe group?

level: principalimportance: nice to knowfreq 20%

answer

  1. probes = Actuator health groups
  2. group.readiness.include = readinessState,db,...
  3. AvailabilityStateHealthIndicator maps custom enum -> Status
  4. additional-path exposes /livez /readyz on server port
  5. never add external checks to liveness

basics

~10 s

The probes are Actuator health groups: 'liveness' includes livenessState and 'readiness' includes readinessState. You can override group membership with management.endpoint.health.group.<name>.include to add other indicators, and set per-group status mappings and roles.

solid answer

~40 s

The liveness/readiness probes are just Actuator **health groups** auto-configured with a single member each: the `livenessState` indicator (backed by `LivenessState`) and the `readinessState` indicator (backed by `ReadinessState`). Because they're normal groups, you can customize them via `management.endpoint.health.group.readiness.include=readinessState,db,customCheck` to fold additional `HealthIndicator`s in — for example adding a message-broker check to readiness so the pod leaves rotation when the broker is down. You can also set `management.endpoint.health.group.<name>.status.http-mapping` and `roles`/`show-details`. For a fully custom availability dimension, you define an enum implementing `AvailabilityState`, drive it with `AvailabilityChangeEvent`, write a `HealthIndicator` (or extend `AvailabilityStateHealthIndicator`) that maps it to a Health status, and include it in the relevant group. Caution: adding external dependency checks to liveness reintroduces restart-storm risk — keep those in readiness.

code

java · 24 lines
java
import org.springframework.boot.actuate.availability.AvailabilityStateHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public enum CacheWarmupState implements AvailabilityState { COLD, WARM }

@Configuration
class WarmupHealthConfig {

    @Bean
    HealthIndicator cacheWarmupHealthIndicator(ApplicationAvailability availability) {
        return new AvailabilityStateHealthIndicator(
            availability, CacheWarmupState.class, mappings -> {
                mappings.add(CacheWarmupState.WARM, Status.UP);
                mappings.add(CacheWarmupState.COLD, Status.OUT_OF_SERVICE);
            });
    }
}
// application.yml:
// management.endpoint.health.group.readiness.include=readinessState,cacheWarmupHealthIndicator

go deeper

for a junior

Know the two default groups exist; customization is beyond scope.

for a middle

Understand probes are health groups and you can add indicators via include.

for a senior

Configure group include, status http-mapping, and know the readiness-vs-liveness placement rule.

for a principal

Design custom availability dimensions with AvailabilityStateHealthIndicator, manage flap/caching, additional-path for split management ports, and fleet-wide restart-blast-radius policy.

## Probes are health groups Actuator has a general feature called **health groups**: named subsets of health indicators exposed at `/actuator/health/<group>`. The Kubernetes probe support is implemented on top of this. When probes are enabled, Spring auto-configures two groups: - `liveness` -> includes the `livenessState` indicator - `readiness` -> includes the `readinessState` indicator Those two indicators (`LivenessStateHealthIndicator`, `ReadinessStateHealthIndicator`) translate the current `AvailabilityState` from `ApplicationAvailability` into an Actuator `Health` (CORRECT/ACCEPTING_TRAFFIC -> UP; BROKEN -> DOWN; REFUSING_TRAFFIC -> OUT_OF_SERVICE). ## Customizing group membership Because they're ordinary groups, you can redefine their `include` list: ```yaml management: endpoint: health: group: readiness: include: readinessState,db,rabbit liveness: include: livenessState ``` Now the readiness probe is DOWN/OUT_OF_SERVICE if the DB or RabbitMQ indicator is down, pulling the pod from the load balancer when a critical dependency is unavailable. **Keep liveness minimal** — do not add dependency checks there. ## Per-group configuration - `management.endpoint.health.group.<name>.show-details` / `show-components` - `management.endpoint.health.group.<name>.roles` — required roles to see details - `management.endpoint.health.group.<name>.status.http-mapping.<status>` — map a status to an HTTP code (e.g., map OUT_OF_SERVICE to 503) - `management.endpoint.health.group.<name>.status.order` — status precedence - Additional path via `management.endpoint.health.group.<name>.additional-path=server:/livez` to also expose the group on the main server port at a custom path (useful when the management port differs from the app port that Kubernetes probes). ## Adding a fully custom availability dimension 1. Define the dimension: ```java public enum CacheWarmupState implements AvailabilityState { COLD, WARM } ``` 2. Drive it with events: `AvailabilityChangeEvent.publish(publisher, this, CacheWarmupState.WARM);` 3. Map it to Health with an `AvailabilityStateHealthIndicator`: ```java @Bean HealthIndicator cacheWarmupHealthIndicator(ApplicationAvailability availability) { return new AvailabilityStateHealthIndicator(availability, CacheWarmupState.class, statusMappings -> { statusMappings.add(CacheWarmupState.WARM, Status.UP); statusMappings.add(CacheWarmupState.COLD, Status.OUT_OF_SERVICE); }); } ``` 4. Include it in the readiness group so warm-up gates traffic. ## Gotchas - **additional-path** matters when management runs on a separate port: Kubernetes typically probes the main app port, so exposing `/livez` and `/readyz` on the server port avoids exposing the whole management port. - Folding heavy checks into readiness can make readiness flap under transient dependency blips; tune indicator caching and probe `failureThreshold`. - Never fold external checks into liveness — a shared dependency outage would restart the entire fleet. - Group `include` overrides the auto-config default, so remember to keep `readinessState` / `livenessState` in the list or you lose the availability-driven behavior.

  • Why might you set management.endpoint.health.group.readiness.additional-path?
    When Actuator runs on a separate management port, Kubernetes probes usually target the main application port. additional-path (e.g. 'server:/readyz') re-exposes the readiness group on the main server port so probes can reach it without opening the whole management port.
  • What's the risk of adding a database HealthIndicator to the readiness group, and how do you contain it?
    Transient DB blips can flap readiness and pull pods in and out of rotation. Contain it with indicator result caching, a higher probe failureThreshold, and by ensuring the check is a lightweight connectivity probe. Never add it to liveness.

saying these in an interview costs you the question

  • Adding external dependency checks to the liveness group
  • Overriding the group include and dropping readinessState/livenessState, losing availability-driven behavior
  • Assuming probes are magic endpoints rather than ordinary Actuator health groups
  • Not accounting for a separate management port when Kubernetes probes the app port

context