skip to content

Health Groups

Health groups bundle a subset of indicators under their own path with their own settings, which is how liveness and readiness get different checks. This is the mechanism behind sensible probe design.

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

questions

5

What is an Actuator health group in Spring Boot, and how do you create one that exposes only a subset of health indicators?

level: juniorimportance: must knowfreq 35%

answer

  1. group.<name>.include / .exclude
  2. path /actuator/health/<name>
  3. exclude beats include
  4. include=* means all
  5. status from group members only

basics

~10 s

A health group bundles a chosen subset of health indicators under a named sub-endpoint. You configure it with management.endpoint.health.group.<name>.include=<indicators>, then read it at /actuator/health/<name>.

solid answer

~40 s

By default /actuator/health aggregates every registered health indicator (db, diskSpace, ping, etc.) into one overall status. A health group lets you carve out a named subset that answers at its own path, /actuator/health/<name>. You declare it purely in properties: `management.endpoint.health.group.custom.include=db,diskSpace` includes those contributors, and `.exclude` removes some (exclude wins over include). `include=*` takes everything then you exclude a few. The group computes its own aggregated status from only its members, so a failing indicator outside the group won't drag the group DOWN. This is the basis for Kubernetes liveness/readiness probes, where each probe should watch a different, targeted set of checks rather than the whole application.

code

yaml · 15 lines
yaml
management:
  endpoints:
    web:
      exposure:
        include: health
  endpoint:
    health:
      group:
        # /actuator/health/custom -> only these two indicators
        custom:
          include: db, diskSpace
        # /actuator/health/light -> everything except the expensive db check
        light:
          include: "*"
          exclude: db

go deeper

for a junior

Know the property shape (group.<name>.include) and the resulting path /actuator/health/<name>.

for a middle

Understand exclude-beats-include, include=*, and that status is computed from group members only.

for a senior

Frame groups as the mechanism enabling distinct K8s liveness/readiness probes over targeted indicator subsets.

for a principal

Reason about isolation guarantees and the operational risk of silently-ignored bad ids masking a non-functioning probe.

## What the health endpoint does by default Spring Boot Actuator's `/actuator/health` endpoint is backed by a `HealthEndpoint` that aggregates every `HealthContributor` bean the app registers — a `DataSourceHealthIndicator` (id `db`), `DiskSpaceHealthIndicator` (`diskSpace`), `PingHealthIndicator` (`ping`), plus any custom ones. It combines their individual statuses into ONE overall `Status` (UP, DOWN, OUT_OF_SERVICE, UNKNOWN) using a `StatusAggregator` — by default the worst status wins (DOWN beats UP). ## The problem groups solve One combined status is too coarse for real deployments. A Kubernetes **liveness** probe should only fail when the app is unrecoverable (restart me), while a **readiness** probe should fail when the app temporarily can't serve traffic (stop routing to me). If both read the same aggregated `/actuator/health`, a flaky external dependency could make Kubernetes *restart* a perfectly alive pod. Groups fix this by letting you expose different, targeted subsets. ## Creating a group Groups are pure configuration — no code, no beans. In `application.yml` / `application.properties`: ``` management.endpoint.health.group.custom.include=db,diskSpace ``` That creates a group named `custom`, reachable at `/actuator/health/custom`. The `<name>` is arbitrary; the value is a comma-separated list of **health indicator ids** (the id is the bean name minus the `HealthIndicator` suffix — `DataSourceHealthIndicator` → `db`). - `include` — the members to add. `*` means "all indicators". - `exclude` — members to remove; **exclude takes precedence over include**, so `include=*` + `exclude=db` = everything except `db`. ## Status is computed from group members only Each group aggregates the status of just its own members. If `diskSpace` is DOWN but it's not in your group, the group stays UP. This isolation is the whole point. ## Gotchas - The group name becomes a URL path segment, so keep it URL-safe (lowercase, no spaces). - Referencing an indicator id that doesn't exist is silently ignored — a typo like `dataSource` instead of `db` means that check simply never runs, so the group can look green while watching nothing. Verify ids by hitting `/actuator/health` with details on. - The health endpoint must itself be exposed (`management.endpoints.web.exposure.include=health`) for any group to be reachable. - Groups are additive to the main endpoint — `/actuator/health` still exists and still aggregates everything. ## When to use Any time different consumers (K8s liveness vs readiness, a load balancer, an internal dashboard) need to watch different slices of health, or when you want a lightweight probe that doesn't touch expensive downstream checks.

  • If an indicator id in your include list is misspelled, what happens?
    It's silently ignored — no error. The group simply won't include that check, so it can report UP while watching fewer indicators than you think. Always verify ids against /actuator/health details.
  • Does creating a group remove indicators from the main /actuator/health?
    No. Groups are additional named sub-endpoints. /actuator/health still aggregates all indicators as before; groups just expose extra, filtered views.

saying these in an interview costs you the question

  • Thinking a group replaces the main /actuator/health endpoint
  • Believing include takes precedence over exclude (it's the reverse)
  • Assuming a group aggregates ALL indicators rather than only its members

context

open as a page

How do you control detail visibility (show-details / show-components) independently for a single health group?

level: middleimportance: should knowfreq 22%

basics

~10 s

Each group accepts its own management.endpoint.health.group.<name>.show-details and .show-components set to never, when-authorized, or always. These override the global management.endpoint.health.show-details for that group only.

open as a page

How can you expose a single health group on the main server port (not the management port) so a Kubernetes probe can reach it, and why would you?

level: seniorimportance: should knowfreq 14%

basics

~10 s

Set management.endpoint.health.group.<name>.additional-path=server:/healthz. That serves the group on the main application port at /healthz, in addition to its normal /actuator/health/<name> path.

open as a page

How do you make a health group return a custom HTTP status code (e.g. map OUT_OF_SERVICE to 503) independently of the main health endpoint?

level: seniorimportance: should knowfreq 18%

basics

~10 s

Use management.endpoint.health.group.<name>.status.http-mapping.<status>=<code>, e.g. map out-of-service to 503. This overrides the global status-to-HTTP mapping for that one group.

open as a page

Where do the built-in liveness and readiness health groups come from, and how do they connect to Spring's ApplicationAvailability?

level: principalimportance: should knowfreq 16%

basics

~10 s

When 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.

open as a page