What is a Spring Boot HealthIndicator and how do you implement one that reports UP or DOWN?
answer
- Health health() single method
- Health.up()/down()/status() + withDetail + build
- bean name minus HealthIndicator = JSON key
- worst status wins; DOWN -> 503
- show-details never/when-authorized/always
basics
~10 sA HealthIndicator is a bean that reports the health of one dependency. You implement the health() method to return Health.up() when things work or Health.down() when they don't, and Spring exposes it at /actuator/health.
solid answer
~40 sHealthIndicator is a functional interface in Spring Boot Actuator with a single method, Health health(). You register an implementation as a Spring bean; Actuator collects all of them and merges their results into the /actuator/health endpoint. Inside health() you run a cheap liveness check (ping a DB, call an API) and return a Health object built with Health.up() or Health.down(). You can attach diagnostic data via .withDetail("key", value) and .build(). The bean name drives the JSON key: a bean named diskSpaceHealthIndicator appears under "diskSpace". The overall endpoint status is the worst of all contributors, and by default DOWN maps to HTTP 503 while UP maps to 200. Details are hidden unless management.endpoint.health.show-details is set to always or when-authorized.
code
java · 28 linesimport org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component // bean name "paymentGatewayHealthIndicator" -> key "paymentGateway"
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final PaymentGatewayClient client;
public PaymentGatewayHealthIndicator(PaymentGatewayClient client) {
this.client = client;
}
@Override
public Health health() {
try {
int latencyMs = client.ping(); // cheap round-trip
return Health.up()
.withDetail("latencyMs", latencyMs)
.build();
} catch (Exception e) {
return Health.down()
.withDetail("reason", "gateway unreachable")
.withException(e)
.build();
}
}
}go deeper
Know the interface (Health health()), the factory methods Health.up()/down(), withDetail, and that it appears at /actuator/health.
Explain bean-name-to-key mapping, aggregation to the worst status, and the DOWN->503 HTTP mapping plus show-details.
Discuss keeping checks cheap/timed, not leaking secrets, exception handling, and when to prefer built-in vs custom indicators.
Frame health signals for orchestration (readiness vs liveness groups), probe frequency cost, and status/HTTP-mapping policy across a fleet.
## What it is `HealthIndicator` is an interface from **Spring Boot Actuator** (`org.springframework.boot.actuate.health`) with one method: ```java Health health(); ``` Actuator is the Spring Boot module that adds production endpoints such as `/actuator/health`, `/actuator/metrics`, and `/actuator/info`. A **health check** answers the question "is this application (and its dependencies) working right now?" Load balancers, Kubernetes liveness/readiness probes, and monitoring systems poll `/actuator/health` and act on the result. ## The Health object `Health` is an immutable value object holding two things: a **`Status`** and a **details map** (`Map<String,Object>`). `Status` wraps a string code; Spring predefines four: `UP`, `DOWN`, `OUT_OF_SERVICE`, and `UNKNOWN`. You never construct `Health` directly — you use the fluent builder returned by static factory methods: - `Health.up()` → status UP - `Health.down()` → status DOWN - `Health.outOfService()` → status OUT_OF_SERVICE - `Health.unknown()` → status UNKNOWN - `Health.status(Status)` / `Health.status(String)` → arbitrary/custom status Each returns a `Health.Builder`, on which you chain: - `.withDetail("key", value)` — add one diagnostic entry - `.withDetails(map)` — add many - `.withException(Throwable)` — record an exception (adds an `"error"` detail) - `.build()` — produce the immutable `Health` ## Registering it Make your implementation a Spring bean (`@Component`, or a `@Bean` method). Actuator auto-detects every `HealthIndicator` bean and registers it as a **health contributor**. The **bean name minus the `HealthIndicator` suffix** becomes the JSON key. So `class MailHealthIndicator` → key `"mail"` in the response: ```json { "status": "UP", "components": { "mail": { "status": "UP" } } } ``` ## How the overall status is computed Actuator aggregates all contributors with a `StatusAggregator` (default `SimpleStatusAggregator`). The default severity order is **DOWN, OUT_OF_SERVICE, UP, UNKNOWN** — the aggregate takes the **most severe** status present. So one DOWN contributor makes the whole endpoint DOWN. `Status` → HTTP code mapping is done by the health endpoint: by default UP and UNKNOWN → **200**, DOWN and OUT_OF_SERVICE → **503 Service Unavailable**. Override with `management.endpoint.health.status.http-mapping.*`. ## Showing details By default the endpoint shows only the top-level status for security. Control exposure with: - `management.endpoint.health.show-details` = `never` (default) | `when-authorized` | `always` - `management.endpoint.health.show-components` — same for the component breakdown ## Gotchas - **Keep it fast and cheap.** `health()` runs on every probe (often every few seconds). Don't do heavy queries; use a timeout. - **Never throw** if you can help it — an uncaught exception still results in DOWN, but you lose control of the details. Prefer catching and calling `.down().withException(e)` (or use `AbstractHealthIndicator`, which does this for you). - **Don't leak secrets** in details (connection strings, credentials) since details can be exposed. ## When to use Write a custom `HealthIndicator` for any external dependency Spring doesn't already cover, or for a business-level readiness signal (e.g., a required cache is warm). Spring already ships auto-configured indicators for DataSource, Redis, Mongo, disk space, mail, etc.
- Why don't the details show up in /actuator/health by default?Because management.endpoint.health.show-details defaults to never, so unauthenticated probes can't see potentially sensitive diagnostics. Set it to when-authorized or always to expose them.
- If one indicator returns DOWN and three return UP, what is the endpoint's overall status and HTTP code?DOWN, because the SimpleStatusAggregator picks the most severe status; DOWN maps to HTTP 503 by default.
saying these in an interview costs you the question
- Thinking health() must throw an exception to signal failure (you return Health.down() instead)
- Believing details are always visible in the response (default is never)
- Assuming the whole endpoint is UP if any single indicator is UP (it's the worst status that wins)
- Doing expensive work in health() that slows every probe