Spring Boot Actuator: Health Checks, Metrics, Info Endpoints, Custom Indicators

Monitor Spring Boot apps with Actuator: built-in health checks, metrics, info endpoints, and how to build custom health indicators.

published: reading time: 24 min read author: GeekWorkBench
Quick Summary

Monitor Spring Boot apps with Actuator: built-in health checks, metrics, info endpoints, and how to build custom health indicators. The guide uses practical examples to explain introduction to spring boot actuator, enabling actuator and endpoint configuration and shows how to apply the ideas in a Spring Boot project. It closes with common pitfalls and production checks so you can apply the pattern with fewer surprises.

Introduction to Spring Boot Actuator

Spring Boot Actuator is a sub-project of Spring Boot that provides production-ready features to help you monitor and manage your application. It offers built-in endpoints that expose operational information about your running application: health status, metrics, environment details, bean listings, and more.

When you deploy an application to production, you need visibility into its internal state. Actuator fills that gap. It works with modern observability stacks and gives your operations team the hooks they need to validate application health, track performance, and respond to incidents.

Actuator gives you:

  • Built-in endpoints for common monitoring needs
  • Extensibility through custom health indicators and metrics
  • Integration with Micrometer for metrics collection
  • Security options to control endpoint exposure
  • Flexibility to expose data in various formats (JSON, HTML)

Enabling Actuator and Endpoint Configuration

Adding Actuator to Your Project

Add the Actuator starter to your pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

Or in build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

Exposing Endpoints

By default, only the health endpoint is visible. You control which endpoints are exposed using the management.endpoints.web.exposure.include property:

# Expose all endpoints
management.endpoints.web.exposure.include=*

# Expose specific endpoints only
management.endpoints.web.exposure.include=health,metrics,info

# Exclude specific endpoints
management.endpoints.web.exposure.exclude=env,beans

Endpoint Configuration

Each endpoint can be configured individually:

# Enable/disable an endpoint
management.endpoint.health.enabled=true
management.endpoint.metrics.enabled=true

# Set cache time-to-live for endpoint responses
management.endpoint.health.cache.time-to-live=10s

# Show/hide details in health endpoint
management.endpoint.health.show-details=always
management.endpoint.health.show-details=when_authorized
management.endpoint.health.show-details=never

Base Path Configuration

Change the base path from /actuator to something else:

management.endpoints.web.base-path=/manage

Health Endpoint Deep Dive

The health endpoint is often the first thing platform teams integrate with load balancers and orchestration tools. It aggregates health information from multiple sources into a single response.

Health Endpoint Response

A typical health response looks like this:

{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "PostgreSQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 512000000000,
        "free": 423000000000,
        "threshold": 104857600
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}

Built-in Health Indicators

Spring Boot auto-configures health indicators for common infrastructure components:

Indicator Activated When Check
DiskSpaceHealthIndicator Always Available disk space above threshold
DataSourceHealthIndicator DataSource bean present Connection pool is valid
RedisHealthIndicator Redis libraries on classpath Redis connection successful
MongoHealthIndicator MongoDB libraries present MongoDB connection successful
RabbitHealthIndicator RabbitMQ libraries present RabbitMQ connection successful
ElasticsearchRestHealthIndicator Elasticsearch client present Elasticsearch cluster reachable
SolrHealthIndicator Solr client present Solr instance reachable

Aggregate Health

When multiple components report health, Spring Boot rolls them up into a final status:

DOWN → OUT_OF_SERVICE → UP

The worst status wins. If any critical component is DOWN, the entire application reports DOWN.

Custom Health Status

You can define custom statuses beyond the default UP and DOWN:

management.health.status.order=DOWN,OUT_OF_SERVICE,UP,UNKNOWN
@Bean
public HealthStatusHttpResultStatusConverter healthStatusHttpResultStatusConverter() {
    return new HealthStatusHttpResultStatusConverter();
}

Metrics Endpoint with Micrometer

Actuator uses Micrometer as its metrics facade. It provides a vendor-neutral interface for collecting metrics.

Accessing Metrics

Query the metrics endpoint:

GET /actuator/metrics/jvm.memory.used
GET /actuator/metrics/http.server.requests
GET /actuator/metrics/process.cpu.usage

Available Metrics

Spring Boot auto-configures numerous metrics across several domains:

JVM Metrics

  • jvm.memory.used - Memory pool usage
  • jvm.memory.max - Maximum memory
  • jvm.gc.pause - GC pause times
  • jvm.threads.states - Thread counts by state

HTTP Metrics

  • http.server.requests - Request count, latency, error rates
  • Tagged with uri, method, status, outcome

Tomcat Metrics

  • tomcat.sessions.active.current - Active sessions
  • tomcat.threads.current - Current threads

Custom Metrics

You can register custom metrics using Micrometer:

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;

@Component
public class CustomMetrics {

    private final Timer orderProcessingTimer;
    private final MeterRegistry meterRegistry;

    public CustomMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.orderProcessingTimer = Timer.builder("order.processing")
            .description("Time taken to process orders")
            .tag("type", "standard")
            .register(meterRegistry);
    }

    public void recordOrderProcessing(Runnable processing) {
        orderProcessingTimer.record(processing);
    }

    public <T> T recordOrderProcessing(Supplier<T> processing) {
        return orderProcessingTimer.recordSupplier(processing);
    }
}

Tags and Dimensions

You can add dimensions to your metrics for filtering:

counter = meterRegistry.counter("api.requests",
    "method", "GET",
    "endpoint", "/users",
    "status", "success");

Prometheus Integration

Expose metrics in Prometheus format:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
management.endpoints.web.exposure.include=health,metrics,prometheus
management.metrics.export.prometheus.enabled=true

Info Endpoint Customization

The info endpoint exposes arbitrary application information.

Auto-populated Info

Spring Boot auto-contributes info when certain dependencies are present:

  • git - Git commit information (requires git-commit-id-plugin)
  • build - Build information (automatic with Maven/Gradle)

Custom Info Contributors

You can implement InfoContributor to add custom data:

import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

@Component
public class CustomInfoContributor implements InfoContributor {

    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("application", Map.of(
            "name", "Order Service",
            "version", "2.1.0",
            "environment", "production"
        ));
    }
}

Info Endpoint Output

{
  "application": {
    "name": "Order Service",
    "version": "2.1.0",
    "environment": "production"
  },
  "git": {
    "commit": {
      "id": "a1b2c3d",
      "time": "2026-06-15T10:30:00Z"
    }
  }
}

Environment Info

management.info.env.enabled=true
management.info.java.enabled=true
management.info.os.enabled=true

Custom Health Indicators Implementation

Custom health indicators let you define what “healthy” means for your specific application.

Implementing a Health Indicator

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class DatabaseHealthIndicator implements HealthIndicator {

    private final DataSource dataSource;

    public DatabaseHealthIndicator(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Health health() {
        try (Connection connection = dataSource.getConnection()) {
            boolean valid = connection.isValid(5);
            if (valid) {
                return Health.up()
                    .withDetail("database", connection.getCatalog())
                    .withDetail("timeout", "5s")
                    .build();
            }
            return Health.down()
                .withDetail("error", "Connection validation failed")
                .build();
        } catch (SQLException e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .withException(e)
                .build();
        }
    }
}

Reactive Health Indicators

For reactive applications, use ReactiveHealthIndicator:

import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@Component
public class ExternalApiHealthIndicator implements ReactiveHealthIndicator {

    private final WebClient webClient;

    @Override
    public Mono<Health> health() {
        return webClient.get()
            .uri("/health")
            .retrieve()
            .toEntity(String.class)
            .map(response -> Health.up().build())
            .onErrorResume(e -> Mono.just(Health.down()
                .withDetail("error", e.getMessage())
                .build()));
    }
}

Grouping Health Indicators

You can define groups of indicators that you check together:

import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HealthGroupConfig {

    @Bean
    public HealthEndpoint.ApplicationSonarHealthDetailsFunction customHealthGroup() {
        return details -> {
            // Custom logic to group health indicators
            return details.getComponents().values().stream()
                .filter(c -> c.getStatus().getCode().equals("UP"))
                .count() > 0;
        };
    }
}
management.endpoint.health.group.custom.include=db,redis,externalApi
management.endpoint.health.group.custom.show-details=always

When to Use / When NOT to Use

When to Use Actuator

Actuator is the right tool when you need:

  • Container orchestration integration: Kubernetes liveness and readiness probes require a reliable health endpoint. Actuator’s /health provides exactly what orchestrators expect.
  • Metrics collection for Grafana/Prometheus: Micrometer integration with Actuator’s /metrics endpoint gives you JVM metrics, HTTP metrics, and custom business metrics in a format your dashboards can consume.
  • Operational visibility without broad access: When you need to give operations teams health and metrics access without granting database logins or server access, Actuator provides a safe read-only channel.
  • Centralized configuration auditing: The /env and /configprops endpoints let you audit what configuration is actually active without peering into production servers.
  • Startup verification: The /info endpoint exposes build git information and version data that helps confirm which version is actually running.

When NOT to Use Actuator

Actuator is the wrong tool when:

  • You need full distributed tracing: Actuator gives you endpoints and numerical summaries, not request traces. Use Spring Cloud Sleuth or OpenTelemetry for distributed tracing.
  • You need detailed profiling: Actuator’s metrics are aggregated counters and timers. For flame graphs and detailed performance profiling, use async-profiler or Java Flight Recorder.
  • You need structured business event logging: Actuator tracks infrastructure metrics. For business events like “order placed” or “user registered”, use structured logging with MDC context.
  • Security sensitive environments with strict compliance: Without careful configuration, /env and /beans endpoints expose internal structure. If you cannot adequately restrict access, leave these endpoints hidden.
  • Real-time streaming of logs: Actuator has no log streaming capability. Use dedicated log aggregation with ELK, Loki, or cloud-native solutions.

Hybrid Approach

Actuator excels at infrastructure monitoring. For complete observability, combine it with distributed tracing (for request flows) and structured logging (for business events). Actuator tells you the app is healthy; tracing tells you why a request was slow; logs tell you what happened.

Common Pitfalls

Exposing Actuator Endpoints Publicly

Avoid exposing all Actuator endpoints in production without authentication:

# WRONG - exposes everything
management.endpoints.web.exposure.include=*

# CORRECT - explicit allowlist
management.endpoints.web.exposure.include=health,metrics,prometheus

Ignoring Health Endpoint Cache

The health endpoint caches its response by default. This can mask transient failures:

# Reduce cache TTL for faster detection of issues
management.endpoint.health.cache.time-to-live=0

Missing Custom Health Indicators for Critical Dependencies

If your application depends on an external service that is not auto-monitored, add a custom health indicator. Do not assume Kubernetes will catch every failure.

Not Configuring Readiness vs Liveness

Mixing up liveness and readiness probes causes confusion:

  • Liveness = Is the application process alive?
  • Readiness = Can the application accept traffic?
management.endpoint.health.group.liveness.include=ping
management.endpoint.health.group.readiness.include=db,redis,mq

Metrics Cardinality Explosion

Adding high-cardinality tags to metrics can cause memory issues:

// WRONG - userId creates unbounded cardinality
meterRegistry.counter("api.calls", "userId", userId);

// CORRECT - use low-cardinality tags
meterRegistry.counter("api.calls", "userType", userType);

Security Notes

Protecting Actuator Endpoints

Option 1: Require Authentication

management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.require-ssl=false
spring.security.basic.enabled=true

Option 2: IP Whitelisting with Firewall

Configure your network firewall or API gateway to restrict access to /actuator/** to only authorized IPs.

Option 3: Custom Security Configuration

@Configuration
@SecurityScheme(
    name = "actuator",
    type = SecuritySchemeType.HTTP,
    scheme = "basic"
)
public class ActuatorSecurityConfig {

    @Bean
    public SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception {
        http.securityMatcher("/actuator/**")
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health").permitAll()
                .requestMatchers("/actuator/**").hasRole("ADMIN")
            );
        return http.build();
    }
}

Sensitive Endpoints

The following endpoints expose sensitive information and should be restricted:

  • /actuator/env - Environment properties
  • /actuator/beans - Application context beans
  • /actuator/configprops - Configuration properties
  • /actuator/heapdump - Heap dump download
# Never expose in production
management.endpoints.web.exposure.exclude=env,beans,configprops,heapdump,threaddump

SSL Configuration

Use HTTPS for Actuator endpoints in production:

management.server.ssl.enabled=true
management.server.ssl.key-store=classpath:keystore.p12
management.server.ssl.key-store-password=${KEYSTORE_PASSWORD}

Implementation Snippets

Prometheus Metrics with Labels

@Service
public class OrderService {

    private final MeterRegistry registry;

    public OrderService(MeterRegistry registry) {
        this.registry = registry;
    }

    public void processOrder(Order order) {
        Timer.Sample sample = Timer.start(registry);

        try {
            // Process order logic
            registry.counter("orders.processed",
                "status", "success",
                "region", order.getRegion()
            ).increment();
        } catch (Exception e) {
            registry.counter("orders.processed",
                "status", "failure",
                "region", order.getRegion(),
                "error", e.getType()
            ).increment();
            throw e;
        } finally {
            sample.stop(Timer.builder("order.processing.time")
                .tag("region", order.getRegion())
                .register(registry));
        }
    }
}

Custom Health Check with Timeout

@Component
public class TimeoutHealthIndicator implements HealthIndicator {

    private final RestTemplate restTemplate;

    @Override
    public Health health() {
        try {
            ResponseEntity<String> response = restTemplate.getForEntity(
                "https://external-api.com/health",
                String.class
            );

            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up().build();
            }
            return Health.down()
                .withDetail("statusCode", response.getStatusCode().value())
                .build();

        } catch (ResourceAccessException e) {
            return Health.down()
                .withDetail("error", "Connection timeout")
                .withDetail("service", "external-api")
                .build();
        }
    }
}

Health Indicator for Database Replication Lag

@Component
public class ReplicationLagHealthIndicator extends AbstractHealthIndicator {

    private final DataSource dataSource;

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        try (Connection connection = dataSource.getConnection();
             Statement stmt = connection.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) FROM pg_stat_replication")) {

            if (rs.next()) {
                long lagBytes = rs.getLong(1);
                long lagMB = lagBytes / (1024 * 1024);

                builder.up().withDetail("replicationLagMB", lagMB);

                if (lagMB > 100) {
                    builder.status("OUT_OF_SERVICE")
                        .withDetail("warning", "Replication lag exceeds threshold");
                }
            } else {
                builder.unknown().withDetail("info", "No replication configured");
            }
        }
    }
}

Multi-step Readiness Check

@Component
public class CompositeReadinessCheck implements HealthIndicator {

    private final List<ReadinessCheck> checks;

    public CompositeReadinessCheck(List<ReadinessCheck> checks) {
        this.checks = checks;
    }

    @Override
    public Health health() {
        Map<String, Health> results = new HashMap<>();
        boolean allHealthy = true;

        for (ReadinessCheck check : checks) {
            Health h = check.check();
            results.put(check.name(), h);
            if (h.getStatus() != Status.UP) {
                allHealthy = false;
            }
        }

        Health.Builder builder = allHealthy ? Health.up() : Health.down();
        builder.withDetails(results.entrySet().stream()
            .collect(Collectors.toMap(
                e -> e.getKey(),
                e -> (Object) e.getValue().getDetails()
            )));

        return builder.build();
    }
}

Observability Checklist

Use this checklist to verify your Actuator implementation is production-ready:

  • Health endpoint returns accurate status based on all critical dependencies
  • Liveness and readiness probes are configured correctly for Kubernetes
  • Metrics are being collected and forwarded to your monitoring system
  • Custom metrics include appropriate tags for filtering and aggregation
  • Info endpoint exposes build and version information for traceability
  • Health endpoint cache TTL is set appropriately for your tolerance
  • Sensitive Actuator endpoints are protected or hidden
  • Prometheus metrics endpoint is available (if using Prometheus)
  • Health groups are defined for different probe types
  • Custom health indicators cover all external service dependencies
  • Health endpoint response times are acceptable under load
  • Logging is configured to include correlation IDs for metrics
  • Alerting is configured based on health status changes
  • Dashboard panels exist for key business metrics from Micrometer

Trade-off Table

Aspect Default Behavior Production Consideration
Health endpoint exposure Only health visible Explicitly list required endpoints
Health details Hidden by default Show for authorized users only
Metrics cache Cached, may hide transients Consider time-to-live=0 for faster alerting
Endpoint base path /actuator May need customization for gateway routing
Auto-configured indicators Covers common infra Always add custom indicators for business dependencies
Cardinality in tags Low cardinality by default Avoid user IDs, request IDs as tags
Decision Pros Cons
Expose all endpoints (*) Simple initial setup Security risk, information disclosure
Detailed health info Better debugging Potential information leakage
High-frequency health checks Faster incident detection Increased load on monitoring infrastructure
Custom health groups Targeted probe checks Additional configuration maintenance

Quick Recap Checklist

  • Add spring-boot-starter-actuator dependency
  • Configure exposed endpoints via management.endpoints.web.exposure.include
  • Implement custom HealthIndicator for business-critical dependencies
  • Set up separate liveness and readiness probe groups
  • Configure health endpoint cache TTL appropriately
  • Add custom metrics with low-cardinality tags via Micrometer
  • Customize info endpoint with build and git information
  • Protect sensitive endpoints with authentication or firewall rules
  • Add health indicators for external service dependencies
  • Test health endpoint response under failure conditions
  • Configure Prometheus metrics export if using Prometheus
  • Set up alerting on health status changes
  • Document which endpoints are exposed and their purpose

Failure Scenarios

Scenario 1: Database Connection Pool Exhaustion

Symptom: Health endpoint returns UP but the application hangs on database operations.

Root Cause: DataSourceHealthIndicator validates connections but does not check pool saturation.

Mitigation:

@Component
public class ConnectionPoolHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        HikariDataSource hikari = (HikariDataSource) dataSource;
        int active = hikari.getHikariPoolMXBean().getActiveConnections();
        int max = hikari.getMaximumPoolSize();

        double utilization = (double) active / max;

        Health.Builder builder = Health.up()
            .withDetail("activeConnections", active)
            .withDetail("maxConnections", max)
            .withDetail("utilization", String.format("%.2f%%", utilization * 100));

        if (utilization > 0.9) {
            return builder
                .status("OUT_OF_SERVICE")
                .withDetail("warning", "Connection pool near capacity")
                .build();
        }

        return builder.build();
    }
}

Scenario 2: Stale Health Information Due to Caching

Symptom: Application reports UP for several seconds after a database failure.

Root Cause: The default health cache TTL is 60 seconds.

Fix:

management.endpoint.health.cache.time-to-live=5s

Scenario 3: Metrics Cardinality Explosion

Symptom: Gradual memory increase in the application, high cardinality in Prometheus.

Root Cause: Custom metrics using high-cardinality tags like userId, sessionId, requestId.

Fix: Use low-cardinality tags, or use MeterFilter to deny high-cardinality metrics:

@Bean
public MeterFilter highCardinalityFilter() {
    return MeterFilter.deny(id -> {
        if (id.getName().startsWith("http.requests")) {
            return id.getTags().stream()
                .anyMatch(tag -> tag.getKey().equals("userId"));
        }
        return false;
    });
}

Interview Questions

1. What is the difference between liveness and readiness probes in Spring Boot Actuator?

Liveness probes determine if the application process is healthy and should be restarted. Readiness probes determine if the application can accept traffic. In Kubernetes terms, a failing liveness probe triggers a pod restart, while a failing readiness probe removes the pod from Service endpoints. With Spring Boot Actuator, you configure these as separate health groups: liveness should be minimal (just ping), while readiness should verify all dependencies like databases, caches, and message queues that are required to handle requests.

2. How do you create a custom health indicator in Spring Boot?

Implement the HealthIndicator interface and annotate with @Component. The interface has a single method health() that returns a Health object. In the method, perform your check logic and return Health.up().withDetail(...).build() on success or Health.down().withDetail(...).build() on failure. Spring Boot registers it automatically with the health endpoint. For reactive applications, implement ReactiveHealthIndicator instead, returning a Mono<Health>.

3. What is Micrometer and how does it integrate with Spring Boot Actuator?

Micrometer is a vendor-neutral metrics facade that provides a unified interface for collecting and exporting metrics. Spring Boot Actuator uses Micrometer as its metrics backend and auto-configures meters for JVM metrics, HTTP metrics, and any infrastructure components present. You access metrics via the /actuator/metrics endpoint. To add custom metrics, inject the MeterRegistry and use its builder methods for counters, timers, gauges, and more.

4. How do you secure Actuator endpoints in a production environment?

Security should be layered. First, use management.endpoints.web.exposure.exclude to hide sensitive endpoints like /env, /beans, and /heapdump. Second, implement authentication on exposed endpoints using Spring Security with a security filter chain matching /actuator/**. Third, restrict IP access at the network level or API gateway. Finally, use HTTPS for production Actuator traffic. The health endpoint can typically be left public for load balancer checks, but metrics and info endpoints should require authentication.

5. What are common pitfalls when using Spring Boot Actuator?

Common pitfalls include: exposing all endpoints publicly via management.endpoints.web.exposure.include=*, relying on default health indicators without adding custom ones for business-critical dependencies, ignoring health endpoint cache settings that mask transient failures, creating high-cardinality metrics tags that cause memory issues, not distinguishing between liveness and readiness probes, and failing to test health indicators under failure conditions. Many teams also forget that the health endpoint only reports component status, not the underlying cause of degradation—for that you need proper logging and distributed tracing.

6. How does health status aggregation work in Spring Boot Actuator?

Health status aggregation follows a worst-status-wins pattern defined by management.health.status.order. The default order is DOWN,OUT_OF_SERVICE,UP,UNKNOWN. When multiple health indicators report status, Spring Boot iterates through this list and returns the first non-UP status it encounters. For example, if any component reports DOWN, the overall status is DOWN. If no component reports DOWN but one reports OUT_OF_SERVICE, the overall status is OUT_OF_SERVICE. Unknown components are ignored in this calculation unless all components are unknown.

7. What is the difference between HealthIndicator and ReactiveHealthIndicator?

HealthIndicator is the synchronous interface used in traditional Spring MVC applications. The health() method blocks while performing its check and returns a Health object directly. ReactiveHealthIndicator is used in reactive (WebFlux) applications and returns a Mono<Health> or Flux<Health>, allowing non-blocking health checks. Spring Boot automatically routes to the appropriate interface based on the application type. You should not implement both in the same component.

8. How do you configure health endpoint cache TTL and why does it matter?

Configure cache TTL via management.endpoint.health.cache.time-to-live. The default is 60 seconds. A non-zero cache means health checks are not performed on every request, reducing overhead, but it also means transient failures may not be immediately visible. Set TTL to 0 for immediate health status updates when you need fast failure detection, or increase it for high-traffic applications where health checks themselves could become a bottleneck. Consider the latency tolerance of your monitoring consumers when setting this value.

9. How do you add custom tags (dimensions) to Micrometer metrics?

Tags (also called dimensions) are added when creating meters via the MeterRegistry. For counters: meterRegistry.counter("api.calls", "method", "GET", "status", "200"). For timers: Timer.builder("request.duration").tag("endpoint", "/users").register(meterRegistry). Tags enable filtering and aggregation in monitoring systems. Always use low-cardinality values—never use userId, sessionId, or requestId as tag values. Common low-cardinality tags include method, uri, status, region, and service.

10. What health indicators are auto-configured by Spring Boot?

Spring Boot auto-configures health indicators when specific dependencies are on the classpath: DataSourceHealthIndicator (any JDBC DataSource), RedisHealthIndicator (Spring Data Redis), MongoHealthIndicator (Spring Data MongoDB), RabbitHealthIndicator (Spring AMQP), ElasticsearchRestHealthIndicator (Elasticsearch client), SolrHealthIndicator (Solr client), DiskSpaceHealthIndicator (always), PingHealthIndicator (always), and others for Couchbase,InfluxDB, Neo4j, and more. Each indicator only activates when its corresponding library is detected.

11. How do you define custom health statuses beyond UP and DOWN?

Add custom statuses by configuring management.health.status.order in your application.properties: management.health.status.order=DOWN,OUT_OF_SERVICE,UP,UNKNOWN,CUSTOM_STATUS. Then in your health indicator, return the custom status: Health.status("CUSTOM_STATUS"). You can also register a HealthStatusHttpResultStatusConverter bean to map custom statuses to specific HTTP response codes for load balancer integration.

12. How does the info endpoint work and what can it expose?

The /actuator/info endpoint exposes arbitrary JSON data from InfoContributor implementations. Spring Boot auto-contributes git information (via git-commit-id-plugin) and build information (Maven/Gradle). Implement InfoContributor to add custom data: call builder.withDetail("key", object) to add any serializable data. Info is read from application.properties with management.info.*.enabled=true flags for java, env, and os details.

13. What is the difference between /actuator/health and /actuator/health/liveness or /actuator/health/readiness?

In Spring Boot 2.3+, enable probes via management.endpoint.health.probes.enabled=true. This exposes /actuator/health/liveness and /actuator/health/readiness as separate endpoints. The liveness probe uses the liveness health group (by default only includes ping), while readiness uses the readiness group (includes db, redis, etc.). Kubernetes uses these to decide pod lifecycle: liveness failures trigger restart, readiness failures prevent traffic routing.

14. How do you test a custom HealthIndicator?

Test health indicators by calling the health() method directly or via the health endpoint. Use mocking to simulate failure conditions: mock the dependency (database, external service) and configure it to throw exceptions or return invalid responses. For integration testing, use @AutoConfigureMockHealthIndicator or test the actual endpoint with TestRestTemplate. Always test both the success path (Health.up()) and failure path (Health.down()), including timeout scenarios.

15. How do you expose metrics in Prometheus format?

Add the Prometheus registry dependency: micrometer-registry-prometheus. Then expose the prometheus endpoint: management.endpoints.web.exposure.include=prometheus. The /actuator/prometheus endpoint returns metrics in Prometheus text format. Configure management.metrics.export.prometheus.enabled=true for additional options like pushgateway integration. Prometheus scrapes this endpoint at regular intervals and stores the time-series data for Grafana visualization.

16. What is Spring Boot Admin and how does it use Actuator?

Spring Boot Admin is an open-source project from codecentric that provides a web UI for monitoring Spring Boot applications. Applications register as clients by adding the spring-boot-admin-client dependency and configuring the admin server URL. The client then pushes Actuator data (health, metrics, env, loggers) to the admin server, which aggregates and displays it. Alternatively, the admin server can use Eureka or Consul for service discovery to find applications to monitor.

17. How do you handle metrics cardinality explosion?

Cardinality explosion occurs when using high-uniqueness tag values like userId, sessionId, or requestId. Prevention: use MeterFilter beans to deny or rename high-cardinality metrics. Example: MeterFilter.deny(id -> id.getTags().stream().anyMatch(t -> t.getKey().equals("userId"))). Alternatively, use low-cardinality tag values like userType (premium/basic) instead of userId. If you need per-request granularity, use distributed tracing (Zipkin, Sleuth) instead of metrics.

18. What is the difference between gauges, counters, and timers in Micrometer?

Gauges report a current value that can go up or down (like memory usage, queue size). Counters always increment (like request count, orders processed). Timers measure duration of events and record both count and total time. Choose based on what you need to track: gauge for "how much now", counter for "how many total", timer for "how long does it take". Counters and timers are preferred for most application metrics as they don't require explicit value updates.

19. How does Actuator integrate with distributed tracing systems like Spring Cloud Sleuth?

When Spring Cloud Sleuth or Spring Cloud OpenTelemetry is on the classpath, Actuator metrics automatically include tracing context (traceId, spanId) as tags via TracingMeterHandler. This allows metrics to be correlated with traces in tools like Zipkin or Jaeger. Health and info endpoints remain separate from tracing—the correlation happens at the metrics level. For Kubernetes environments, tracing context propagation ensures that spans from different pods share the same trace ID.

20. What endpoints should never be exposed in production and why?

Never expose /actuator/env (full environment properties including secrets), /actuator/beans (lists all Spring beans and their dependencies), /actuator/configprops (configuration property names and values), /actuator/heapdump (heap dump download containing sensitive data in memory), and /actuator/threaddump (thread dumps can reveal business logic in stack traces). Always exclude these or protect them behind authentication: management.endpoints.web.exposure.exclude=env,beans,configprops,heapdump,threaddump.

Further Reading

Topic-Specific Deep Dives

  • Spring Boot Actuator in Kubernetes: Configure liveness and readiness probes for optimal Kubernetes deployment. Spring Boot 2.3+ provides built-in support for probe configuration via management.endpoint.health.probes.enabled=true. The health endpoint adapts its response based on probe type, returning 200 OK for liveness and 503 Service Unavailable when readiness fails.

  • Micrometer Deep Dive: Micrometer is the metrics facade behind Actuator’s /metrics endpoint. It supports multiple registries: Prometheus, Datadog, Graphite, InfluxDB, and more. Learn how to create composite meters using CompositeMeterRegistry and how MeterBinder beans auto-register metrics at startup.

  • Prometheus & Grafana Integration: Expose Prometheus-format metrics via the /actuator/prometheus endpoint. Configure PrometheusMeterRegistry with custom collectors for business metrics. In Grafana, use the Prometheus datasource and import the JVM micorservices dashboard for instant visibility into JVM behavior under production load.

  • Spring Boot Admin: A community project that provides a web UI for monitoring Spring Boot applications. It registers as a client to Actuator endpoints and aggregates health, metrics, and environment information into a centralized dashboard with alerting capabilities.

  • Health Indicator Groups: Beyond the default health aggregation, you can define custom groups with specific include lists and show-details policies. This is particularly useful when different consumers need different health perspectives: Kubernetes probes versus human operators versus external monitoring systems.

  • Customizing the Health Response: Override HealthEndpointResponse to add additional fields, change the status code mapping, or add headers like X-Health-Check for load balancer integration. The HealthResultStatusHttpCodeConverter controls which HTTP status codes map to which health statuses.

External Resources

Conclusion

Spring Boot Actuator transforms raw application internals into actionable production intelligence. The health endpoint gives orchestrators and load balancers the signals they need, while Micrometer metrics feed observability platforms like Prometheus and Grafana. Custom health indicators let you model your application’s specific failure modes rather than relying on generic checks.

Key takeaways: expose only what you need, protect sensitive endpoints, use health groups to separate liveness from readiness probes, and always test your health indicators under failure conditions. Actuator is not a replacement for distributed tracing or structured logging, but it is the foundation that makes those tools effective in a Spring Boot environment.

Category

Related Posts

Spring Boot Build Tools: Maven & Gradle

Configure Maven and Gradle for Spring Boot projects—plugins, dependency management, packaging JARs and WARs, and build automation essentials.

#spring-boot #spring-boot-roadmap #learning-path

Embedded Web Servers in Spring Boot: Tomcat, Jetty, Undertow

Configure embedded servers in Spring Boot: compare Tomcat, Jetty, and Undertow, tune thread pools, enable access logs, and switch implementations.

#spring-boot #spring-boot-roadmap #learning-path

JUnit 5 & Jupiter: Lifecycle, Nested & Parameterized Tests

Explore JUnit 5 Jupiter features: master test lifecycle annotations, organize tests with @Nested, and parameterize tests with @CsvSource and @MethodSource.

#spring-boot #spring-boot-roadmap #learning-path