Interview Self‑Test: Spring Boot Actuator Health Checks – Quick Review & Must‑Know Answers

This article provides a comprehensive interview self‑test covering Spring Boot Actuator’s built‑in endpoints, health‑check JSON format, status aggregation logic, custom HealthIndicator implementation, show‑details configuration, @Endpoint operation annotations, liveness vs. readiness probes, handling of DOWN status, disabling auto‑configured indicators, security risks of exposing all endpoints, HealthIndicatorRegistry naming rules, additional Actuator endpoints, CompositeHealth construction, and custom HealthStatusHttpMapper mapping.

CodeSmart Hoops
CodeSmart Hoops
CodeSmart Hoops
Interview Self‑Test: Spring Boot Actuator Health Checks – Quick Review & Must‑Know Answers

Built‑in Actuator endpoints

/actuator/health

(GET) – health check, returns overall status and component health. /actuator/info (GET) – basic application information such as version and build time. /actuator/beans (GET) – lists all Spring beans and their dependencies. /actuator/env (GET, POST) – inspects all environment properties and allows dynamic modification. /actuator/metrics (GET) – lists metric names; /actuator/metrics/{name} returns a specific metric value. /actuator/loggers (GET, POST) – shows current logger levels and permits runtime changes. /actuator/threaddump (GET) – thread snapshot for diagnosing deadlocks and blocking. /actuator/conditions (GET) – auto‑configuration report indicating which configurations are active. /actuator/httpexchanges (GET) – recent HTTP request/response trace. /actuator/scheduledtasks (GET) – registered @Scheduled tasks. /actuator/caches (GET, DELETE) – cache management, view and clear caches.

Health endpoint JSON structure

{
  "status": "DOWN",
  "components": {
    "diskSpace": {
      "status": "UP",
      "details": {"total": 500105211904, "free": 100234567890, "threshold": 10485760}
    },
    "ping": {"status": "UP"},
    "redis": {"status": "DOWN", "details": {"error": "Connection refused: no further information"}},
    "db": {"status": "UP", "details": {"database": "MySQL", "hello": 1}}
  }
}

The top‑level status is the aggregated health derived by HealthAggregator. Each entry in components corresponds to a registered HealthIndicator and contains its own status and optional details. HTTP status 200 is returned when the overall status is UP; otherwise 503 is returned.

HealthAggregator status ordering logic

Statuses are sorted according to the default order DOWN, OUT_OF_SERVICE, UNKNOWN, UP. The first element after sorting becomes the overall status, implementing a one‑vote‑veto rule: any DOWN component forces the overall status to DOWN. The order can be overridden via management.endpoint.health.status.order.

public Health aggregate(Set<Status> statuses) {
    statuses.sort((s1, s2) -> {
        int o1 = this.statusOrder.indexOf(s1.getCode());
        int o2 = this.statusOrder.indexOf(s2.getCode());
        return Integer.compare(o1, o2);
    });
    Status finalStatus = statuses.iterator().next();
    return new Health.Builder(finalStatus).build();
}

Creating a custom HealthIndicator

Two approaches are shown:

Implement HealthIndicator directly and annotate the class with @Component. The health() method returns Health.up() or Health.down() with optional details.

Extend AbstractHealthIndicator for more complex checks and override doHealthCheck(Health.Builder).

@Component
public class RedisHealthIndicator implements HealthIndicator {
    private final String redisHost = "localhost";
    private final int redisPort = 6379;
    @Override
    public Health health() {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(redisHost, redisPort), 1000);
            return Health.up()
                .withDetail("server", redisHost + ":" + redisPort)
                .withDetail("responseTime", "connected")
                .build();
        } catch (Exception e) {
            return Health.down()
                .withDetail("server", redisHost + ":" + redisPort)
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

Key points: add @Component for auto‑registration, bean name suffix HealthIndicator is stripped to form the component name, and return Health.up() or Health.down() with withDetail() for extra data.

show‑details configuration

never

(default) – only status is returned; details are hidden. when-authenticated – details are shown to authenticated users (typically used with Spring Security). always – details are always included, suitable for internal tools.

Example (application.properties): management.endpoint.health.show-details=always The logic resides in HealthEndpointWebExtension, which checks the showDetails setting before exposing the details map.

@Endpoint operation annotations

@ReadOperation

– GET – read data from the endpoint. @WriteOperation – POST – modify data (e.g., change log level). @DeleteOperation – DELETE – delete or reset data (e.g., clear caches).

During startup, EndpointHandlerMapping scans all @Endpoint beans and creates URL mappings such as:

GET    /actuator/appinfo   → @ReadOperation
POST   /actuator/appinfo   → @WriteOperation
DELETE /actuator/appinfo   → @DeleteOperation

Liveness vs. Readiness probes

Check target : Liveness checks the application process itself (JVM); Readiness checks service dependencies (DB, Redis, MQ, etc.).

Failure consequence : Liveness failure causes Kubernetes to restart the container; Readiness failure causes Kubernetes to remove the pod from the Service without a restart.

Typical Actuator endpoints : Liveness – /actuator/health/liveness; Readiness – /actuator/health/readiness.

Typical misconfiguration: using the same health endpoint for both probes, which can cause endless restarts if a dependency is down.

Correct configuration example:

# Liveness – only app checks
management.endpoint.health.group.liveness.include=app
# Readiness – all dependencies
management.endpoint.health.group.readiness.include=redis,db,diskSpace,ping

Kubernetes pod spec snippet:

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 5

Health DOWN does not stop the JVM

When a component reports DOWN, the overall health is DOWN and the HTTP response code is 503, but the JVM and web container keep running. Other application endpoints (e.g., /api/users) still return 200.

Application process (JVM)─── still running
    ├── Web container (Tomcat)─── still handling requests
    └── HealthEndpoint ────── returns status: DOWN
        ├── diskSpace: UP
        └── redis: DOWN      ← this causes overall DOWN

Disabling auto‑configured HealthIndicators

Set the corresponding property to false. Example:

# Disable Redis health check
management.health.redis.enabled=false
# Disable DB health check
management.health.db.enabled=false
# Disable MongoDB health check
management.health.mongo.enabled=false
# Disable RabbitMQ health check
management.health.rabbit.enabled=false

Note: DiskSpaceHealthIndicator cannot be disabled because Spring Boot treats disk space as essential.

Security risk of exposing * in production

/actuator/env

– exposes all environment variables and configuration properties, including passwords and API keys. /actuator/beans – reveals the full bean graph, aiding attackers in mapping the application architecture. /actuator/loggers – can be set to DEBUG to cause log‑file exhaustion (DoS). /actuator/httpexchanges – records recent request/response data, potentially containing tokens. /actuator/threaddump – exposes thread stack traces.

Correct practice: expose only required endpoints and protect the rest with Spring Security.

# Expose only safe endpoints
management.endpoints.web.exposure.include=health,info,metrics
# Secure sensitive endpoints
http.authorizeHttpRequests(auth -> auth
    .requestMatchers("/actuator/health").permitAll()
    .requestMatchers("/actuator/**").hasRole("ADMIN")
    .anyRequest().authenticated());

HealthIndicatorRegistry registration and naming rules

During auto‑configuration, Spring scans all HealthIndicator beans and registers them in a ConcurrentHashMap<String, HealthContributor>. Bean names ending with HealthIndicator have the suffix stripped to form the component name (e.g., redisHealthIndicatorredis). If the bean name does not end with the suffix, the full bean name is used.

Example verification:

@Test
void healthIndicatorNaming() {
    Map<String, HealthIndicator> indicators = context.getBeansOfType(HealthIndicator.class);
    assertTrue(indicators.containsKey("diskSpaceHealthIndicator"));
    // In the health response the component name is "diskSpace"
}

Additional Actuator endpoints (at least five)

/actuator/metrics

– list and query metric values. /actuator/loggers – view and change logger levels at runtime. /actuator/env – inspect and modify environment properties. /actuator/beans – display bean definitions and dependencies. /actuator/threaddump – thread snapshot for debugging. /actuator/conditions – auto‑configuration report. /actuator/httpexchanges – recent HTTP exchange trace. /actuator/caches – view and clear caches. /actuator/scheduledtasks – list registered scheduled tasks.

Dynamic log‑level adjustment example:

curl -X POST http://localhost:8080/actuator/loggers/com.example \
  -H "Content-Type: application/json" \
  -d '{"configuredLevel":"DEBUG"}'

CompositeHealth construction process

HealthEndpoint iterates over all HealthIndicator beans.

Each indicator’s health() is called.

Results are added to a CompositeHealth.Builder via withComponent(name, health), which also updates the aggregated status.

The builder sorts statuses and determines the overall status. build() creates a CompositeHealth instance, merging component details.

The endpoint mapper converts the CompositeHealth to the HTTP response.

public static class Builder extends Health.Builder {
    private final Map<String, Health> components = new LinkedHashMap<>();
    public Builder withComponent(String name, Health health) {
        this.components.put(name, health);
        Status status = health.getStatus();
        status(status); // triggers sorting logic
        return this;
    }
    @Override
    public Health build() {
        CompositeHealth health = new CompositeHealth(this);
        for (Map.Entry<String, Health> entry : components.entrySet()) {
            withDetail(entry.getKey(), entry.getValue().getDetails());
        }
        return health;
    }
}

Unit tests verify that all‑UP components yield an overall UP, while any DOWN component forces the overall status to DOWN.

Customizing HealthStatusHttpMapper

By default, UP maps to HTTP 200 and any other status maps to 503. A custom bean can implement HealthStatusHttpMapper to change this mapping, for example returning 200 for a custom warning status.

@Bean
public HealthStatusHttpMapper statusHttpMapper() {
    return new CustomStatusHttpMapper();
}

class CustomStatusHttpMapper implements HealthStatusHttpMapper {
    @Override
    public int mapStatus(Status status) {
        return switch (status.getCode()) {
            case "UP" -> 200;
            case "DOWN", "OUT_OF_SERVICE" -> 503;
            case "CUSTOM_WARNING" -> 200; // treat warning as healthy for front‑end logic
            default -> 503;
        };
    }
}

Use cases include aligning HTTP codes with front‑end expectations or distinguishing different unhealthy levels.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaKubernetesSpring BootHealth CheckActuator
CodeSmart Hoops
Written by

CodeSmart Hoops

A working programmer who loves coding and basketball. By day I debug code; by night I dissect tactics. I write articles to document my journey, focusing on Java, AI, Python and other programming topics, with occasional posts about basketball, English, and books. Hope it's helpful—thanks for following and support.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.