Health Indicators
IntermediateBuilt-in health indicators report the status of DB, Kafka, Redis, and disk; custom indicators implement HealthIndicator and are aggregated into a composite UP/DOWN.
Overview
Spring Boot Actuator's /actuator/health endpoint aggregates the health of all registered HealthIndicator beans into a single composite status (UP, DOWN, OUT_OF_SERVICE, UNKNOWN). Built-in indicators cover the most common dependencies: DataSource (ping query), Kafka (AdminClient describe cluster), Redis (PING), RabbitMQ (connection check), and disk space. Custom indicators let you check business-critical dependencies (external APIs, license servers, downstream services). Kubernetes uses /actuator/health/liveness and /actuator/health/readiness as separate probes: liveness for crash detection, readiness to control traffic admission. Exposing sensitive health details requires the management.endpoint.health.show-details=always setting with proper security controls.
Actuator health endpoint and configuration
Enable and expose the health endpoint; configure detail visibility and Kubernetes liveness/readiness groups.
# application.properties
management.endpoint.health.enabled=true
management.endpoints.web.exposure.include=health,info,metrics,prometheus
# Show full health details (component breakdown)
# Restrict to ADMIN role in production; "when_authorized" uses security
management.endpoint.health.show-details=always
# Or: only show details for authenticated ADMIN users
# management.endpoint.health.show-details=when_authorized
# Kubernetes liveness and readiness probes
management.endpoint.health.probes.enabled=true
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
# /actuator/health/liveness → {status: "LIVE" or "BROKEN"}
# /actuator/health/readiness → {status: "ACCEPTING_TRAFFIC" or "REFUSING_TRAFFIC"}
# Kubernetes probe config (deployment.yaml)
# livenessProbe:
# httpGet: { path: /actuator/health/liveness, port: 8080 }
# initialDelaySeconds: 30
# periodSeconds: 10
# readinessProbe:
# httpGet: { path: /actuator/health/readiness, port: 8080 }
# initialDelaySeconds: 10
# periodSeconds: 5Custom HealthIndicator
Implement HealthIndicator to add a business-specific health check. The component name becomes the key in the health response.
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final PaymentGatewayClient client;
@Override
public Health health() {
try {
PingResponse response = client.ping();
if (response.isOk()) {
return Health.up()
.withDetail("latency_ms", response.getLatencyMs())
.withDetail("version", response.getApiVersion())
.build();
} else {
return Health.down()
.withDetail("reason", "Ping returned: " + response.getStatus())
.build();
}
} catch (Exception e) {
return Health.down()
.withException(e)
.withDetail("url", client.getBaseUrl())
.build();
}
}
}
// Health response at /actuator/health:
// {
// "status": "UP",
// "components": {
// "paymentGateway": { "status": "UP", "details": {"latency_ms": 42} },
// "db": { "status": "UP" },
// "redis": { "status": "UP" }
// }
// }ReactiveHealthIndicator and health groups
For WebFlux apps use ReactiveHealthIndicator. Health groups let you include/exclude specific indicators per probe (liveness should not include DB).
# Health groups: liveness should only check in-process state (not DB)
# If DB is down, pod is not "crashed" — readiness handles that
management.endpoint.health.group.liveness.include=livenessState
management.endpoint.health.group.readiness.include=readinessState,db,redis,kafka
# Custom group for internal monitoring (show all)
management.endpoint.health.group.internal.include=*
management.endpoint.health.group.internal.show-details=always
// ReactiveHealthIndicator for WebFlux
@Component
public class ExternalApiHealthIndicator implements ReactiveHealthIndicator {
private final WebClient webClient;
@Override
public Mono<Health> health() {
return webClient.get()
.uri("/health")
.retrieve()
.bodyToMono(String.class)
.map(body -> Health.up().withDetail("response", body).build())
.timeout(Duration.ofSeconds(2))
.onErrorReturn(Health.down()
.withDetail("reason", "Timeout or connection refused").build());
}
}Key Points to Remember
- 1Liveness probe: is the JVM alive? Should only check in-process state. Down → pod restarts.
- 2Readiness probe: can the pod serve traffic? Should check DB, Redis, Kafka. Down → pod removed from LB.
- 3Never include database in liveness — a slow DB query would restart healthy pods.
- 4show-details=when_authorized requires the caller to be authenticated; use this in production to hide internals.
- 5Custom HealthIndicators are auto-discovered as Spring beans; name the component to control the JSON key.
- 6Health status aggregation: one DOWN component makes the composite status DOWN unless explicitly excluded.
Interview Questions
Sign in to ask AriaWhat is the difference between a liveness probe and a readiness probe in Kubernetes?
Why should you NOT include database health in the liveness probe?
How do you implement a custom health indicator that checks an external API?
How would you configure separate health groups for liveness and readiness in Spring Boot?
What happens to the composite health status when one HealthIndicator returns DOWN?
Ask Aria about Health Indicators
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.