Why this matters
- Kubernetes liveness and readiness probes need a reliable endpoint that returns 200 only when the app can actually handle requests.
- Custom health indicators let you surface BookStore-specific dependencies: database connectivity, Redis reachability, and payment gateway status.
- Metrics endpoints feed Prometheus and Grafana dashboards — request rates, error ratios, and JVM memory without writing instrumentation from scratch.
Logs
Logback + MDCStructured JSON
Metrics
MicrometerCounters, timers
Traces
OpenTelemetryTrace propagation
Actuator endpoints you will use daily
/actuator/health— aggregated health with component breakdown (db,redis,diskSpace)./actuator/metrics— lists available Micrometer meters; append a name for current values./actuator/info— build metadata frominfo.*properties and customInfoContributorbeans./actuator/prometheus— scrape-friendly metrics export when the Prometheus registry is on the classpath.HealthIndicator— implement this interface to add custom checks to the health aggregate.
Add Actuator and expose endpoints
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when_authorized
probes:
enabled: true # adds /actuator/health/liveness and /readiness
info:
env:
enabled: true
info:
app:
name: BookStore API
version: 2.4.1
Custom health for external dependencies
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final PaymentGatewayClient paymentClient;
public PaymentGatewayHealthIndicator(PaymentGatewayClient paymentClient) {
this.paymentClient = paymentClient;
}
@Override
public Health health() {
try {
paymentClient.ping();
return Health.up()
.withDetail("gateway", "stripe")
.withDetail("latencyMs", paymentClient.lastPingLatency())
.build();
} catch (Exception ex) {
return Health.down()
.withDetail("gateway", "stripe")
.withDetail("error", ex.getMessage())
.build();
}
}
}
@Component
public class CatalogHealthContributor implements HealthContributor {
private final BookRepository bookRepository;
@Override
public Health health() {
long count = bookRepository.count();
if (count == 0) {
return Health.outOfService()
.withDetail("reason", "catalog empty — seed data missing")
.build();
}
return Health.up().withDetail("bookCount", count).build();
}
}
Secure actuator in production
@Configuration
public class ActuatorSecurityConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception {
return http
.securityMatcher("/actuator/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/actuator/health/**").permitAll()
.requestMatchers("/actuator/prometheus").hasRole("MONITORING")
.anyRequest().hasRole("ADMIN"))
.httpBasic(Customizer.withDefaults())
.build();
}
}
Quick recall
Everything you need if you only revisit this box.
spring-boot-starter-actuatorauto-configures health, metrics, and info endpoints.- Expose only what you need via
management.endpoints.web.exposure.include. - Implement
HealthIndicatorfor BookStore-specific dependency checks. - Use
OUT_OF_SERVICEwhen the app is running but should not receive traffic (empty catalog, maintenance mode). - Lock down actuator endpoints in production; leave health probes public for orchestrators.
Test yourself
Answer these before moving on — recall is what makes it stick.