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.
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 → @DeleteOperationLiveness 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,pingKubernetes 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: 5Health 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 DOWNDisabling 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=falseNote: 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., redisHealthIndicator → redis). 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
