Spring Boot
Health Check
Custom Health Check
Application Monitoring
Java Development

How to add a custom health check in spring boot health?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In Spring Boot, a custom health check is usually implemented as a HealthIndicator bean that contributes to the Actuator health endpoint. This is the right approach when you need to report the status of something application-specific, such as an external API, a queue, a license check, or a custom readiness condition. The important part is to keep the health check cheap, deterministic, and operationally meaningful.

Add Actuator First

A custom health check plugs into Spring Boot Actuator, so the project needs the Actuator starter.

Maven:

xml
1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-actuator</artifactId>
4</dependency>

Once included, Spring Boot exposes health information through the Actuator health endpoint, subject to your endpoint exposure configuration.

Implement HealthIndicator

The most common custom check is a bean that implements HealthIndicator.

java
1import org.springframework.boot.actuate.health.Health;
2import org.springframework.boot.actuate.health.HealthIndicator;
3import org.springframework.stereotype.Component;
4
5@Component
6public class ExternalApiHealthIndicator implements HealthIndicator {
7
8    @Override
9    public Health health() {
10        boolean apiReachable = true;
11
12        if (apiReachable) {
13            return Health.up()
14                .withDetail("externalApi", "reachable")
15                .build();
16        }
17
18        return Health.down()
19            .withDetail("externalApi", "unreachable")
20            .build();
21    }
22}

Once this bean is registered, Spring Boot automatically includes it in the health report.

Example: Check a Service Dependency

An example is probing a dependency through a client.

java
1import org.springframework.boot.actuate.health.Health;
2import org.springframework.boot.actuate.health.HealthIndicator;
3import org.springframework.stereotype.Component;
4
5@Component
6public class BillingHealthIndicator implements HealthIndicator {
7
8    private final BillingClient billingClient;
9
10    public BillingHealthIndicator(BillingClient billingClient) {
11        this.billingClient = billingClient;
12    }
13
14    @Override
15    public Health health() {
16        try {
17            billingClient.ping();
18            return Health.up().withDetail("billing", "ok").build();
19        } catch (Exception ex) {
20            return Health.down(ex).withDetail("billing", "unavailable").build();
21        }
22    }
23}

This pattern is common for dependencies that have a lightweight ping or status call.

Keep Health Checks Lightweight

Health endpoints may be polled frequently by:

  • load balancers
  • orchestration platforms
  • uptime monitors
  • dashboards

That means the check should be:

  • fast
  • low-cost
  • safe to call repeatedly

Do not turn a health check into a slow business workflow or a heavy full-database scan.

Distinguish Liveness from Readiness

Operationally, not every failing dependency should necessarily mark the whole service as dead. A useful mental model is:

  • liveness: should this process be restarted
  • readiness: should this instance receive traffic

Depending on your deployment model, a custom health check might be more appropriate for readiness than for liveness. That design choice matters more than the Java interface itself.

Expose Details Carefully

Health responses can include details:

java
1return Health.up()
2    .withDetail("queueDepth", 12)
3    .withDetail("status", "healthy")
4    .build();

That is useful for operators, but be careful not to leak sensitive information such as credentials, internal endpoints, or exception payloads you would not want exposed externally.

Reactive Applications

If the application stack is reactive, Spring Boot also supports reactive health contributors. For many standard synchronous apps, HealthIndicator is still the usual choice. The key point is that your health-check style should match the execution model of the app rather than blocking reactive threads with slow synchronous probes.

Configuration and Visibility

Depending on your Spring Boot version and security setup, you may need to expose the health endpoint explicitly.

Example:

properties
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=always

Use show-details carefully in production. It is useful during development or internal operations, but not always appropriate for public exposure.

Testing the Health Indicator

A simple unit test can verify the indicator behavior.

java
1import org.junit.jupiter.api.Test;
2import static org.assertj.core.api.Assertions.assertThat;
3
4class ExternalApiHealthIndicatorTest {
5
6    @Test
7    void returnsUpWhenReachable() {
8        ExternalApiHealthIndicator indicator = new ExternalApiHealthIndicator();
9        assertThat(indicator.health().getStatus().getCode()).isEqualTo("UP");
10    }
11}

For real dependency checks, mocking the client is usually the better test strategy.

Common Pitfalls

The biggest mistake is making the health check too slow or too expensive for frequent polling. Another is treating every dependency issue as a process-death condition when the operational semantics may really be about readiness. Developers also often expose too much detail in health responses. Finally, if the custom indicator bean is not registered or the Actuator endpoint is not exposed, the health check may be correct in code but invisible in practice.

Summary

  • Implement a custom HealthIndicator bean to add application-specific health logic.
  • Keep the check fast, cheap, and safe for repeated polling.
  • Use health details carefully and avoid leaking sensitive information.
  • Think operationally about liveness versus readiness.
  • Make sure Actuator is included and the health endpoint is actually exposed.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.