skip to content

HealthIndicator & HealthContributor

A HealthIndicator returns up, down or a custom status with details, and contributors compose into the overall result. Interviewers ask what belongs in a health check, since checking every downstream makes your health depend on everyone else's.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is a Spring Boot HealthIndicator and how do you implement one that reports UP or DOWN?

level: juniorimportance: must knowfreq 70%

answer

  1. Health health() single method
  2. Health.up()/down()/status() + withDetail + build
  3. bean name minus HealthIndicator = JSON key
  4. worst status wins; DOWN -> 503
  5. show-details never/when-authorized/always

basics

~10 s

A 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 s

HealthIndicator 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 lines
java
import 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

for a junior

Know the interface (Health health()), the factory methods Health.up()/down(), withDetail, and that it appears at /actuator/health.

for a middle

Explain bean-name-to-key mapping, aggregation to the worst status, and the DOWN->503 HTTP mapping plus show-details.

for a senior

Discuss keeping checks cheap/timed, not leaking secrets, exception handling, and when to prefer built-in vs custom indicators.

for a principal

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

context

open as a page

What does AbstractHealthIndicator give you over implementing HealthIndicator directly, and how does doHealthCheck work?

level: middleimportance: should knowfreq 55%

basics

~20 s

AbstractHealthIndicator is a base class that implements health() for you and wraps your check in a try/catch. You override doHealthCheck(Health.Builder), and if it throws, the base class automatically sets status DOWN and records the exception.

open as a page

A custom HealthIndicator returns Health.status("DEGRADED"). Explain how that status affects the endpoint's overall status and HTTP code, and how to surface the details.

level: middleimportance: should knowfreq 45%

basics

~20 s

A custom status like DEGRADED is unknown to Spring's default aggregator, so it's treated as least severe and won't drag the endpoint down, and by default it maps to HTTP 200. To make it meaningful you must configure the status order and its HTTP mapping, and set show-details to see the details.

open as a page

How do HealthContributor and CompositeHealthContributor let you group and nest health checks?

level: seniorimportance: should knowfreq 40%

basics

~20 s

HealthContributor is the marker interface that HealthIndicator implements. A CompositeHealthContributor bundles several named contributors under one parent, so the endpoint shows a nested tree — for example one "externalApis" node with a child per API.

open as a page

How do reactive health checks differ from blocking ones, and what happens to a blocking HealthIndicator in a WebFlux app?

level: principalimportance: should knowfreq 35%

basics

~20 s

In a reactive (WebFlux) app you implement ReactiveHealthIndicator, whose health() returns Mono<Health> instead of a blocking Health. Existing blocking HealthIndicators still work — Spring adapts them and runs their code on a bounded elastic scheduler so they don't block the event loop.

open as a page