Skip to content

Spring Boot Actuator in Depth

The Automobile Dashboard Analogy

Imagine purchasing a high performance automobile that has no dashboard instruments: no speedometer, no fuel gauge, no engine temperature warning light, and no oil pressure monitor. The car still accelerates, steers, and brakes. But the driver operates completely blind. You cannot tell if the engine is overheating on the highway until smoke pours from the hood, and you cannot verify fuel levels until the engine stalls on an interstate bridge.

In software engineering, running Spring Boot in production without Spring Boot Actuator is driving that dashboardless automobile. Your REST endpoints may handle requests, but you have zero visibility into JVM memory utilization, database connection pool health, active thread counts, garbage collection pauses, or logging levels.

Spring Boot Actuator provides production grade, pre built management endpoints out of the box. It acts as the vehicle dashboard for your microservice, allowing operations teams, container orchestrators (Kubernetes), and monitoring platforms (Prometheus, Grafana, Datadog) to observe, audit, and interact with the application while it runs.

This lecture covers Actuator configuration, essential endpoints, writing custom HealthIndicator components, runtime log level mutation, and securing actuator endpoints in production.


Adding Spring Boot Actuator

Add the Actuator dependency to your pom.xml:

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

When you start the application, Actuator automatically registers management endpoints under the base path /actuator.


The Essential Actuator Endpoints

By default, for security reasons, Spring Boot exposes only /actuator/health over HTTP. All other endpoints must be explicitly enabled and exposed in application.properties.

EndpointDefault ExposurePurpose & Diagnostic Value
/actuator/healthEnabledShows application health status (UP, DOWN). Used by Kubernetes liveness and readiness probes
/actuator/infoDisabledDisplays arbitrary application information (build version, git commit hash, description)
/actuator/metricsDisabledExposes internal JVM, memory, GC, HTTP request, and thread metrics
/actuator/envDisabledExposes properties from Spring Environment, system properties, and environment variables
/actuator/beansDisabledDisplays a complete list of all Spring beans instantiated in the ApplicationContext
/actuator/loggersDisabledDisplays and modifies logging levels of specific packages at runtime without restarting
/actuator/threaddumpDisabledGenerates an instantaneous JVM thread dump for diagnosing deadlocks and CPU spikes

Configuring Endpoint Exposure in application.properties

You control which endpoints are exposed over HTTP using management.endpoints.web.exposure.include:

properties
server.port=8080
spring.application.name=order-service

# Expose specific management endpoints over HTTP
management.endpoints.web.exposure.include=health,info,metrics,loggers

# To expose all endpoints (strictly for local development, NEVER in production!):
# management.endpoints.web.exposure.include=*

# Change actuator base path if desired (default is /actuator)
management.endpoints.web.base-path=/actuator

# Show detailed health breakdown to authenticated clients
management.endpoint.health.show-details=always

Deep Dive: /actuator/health

When you visit http://localhost:8080/actuator/health, Actuator evaluates health indicators registered across the application:

json
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "PostgreSQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 499963174912,
        "free": 321854218240,
        "threshold": 10485760,
        "path": "/var/log",
        "exists": true
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}

If even one critical component reports DOWN, the overall application status transitions to DOWN and Actuator returns an HTTP 503 Service Unavailable status code, signaling to Kubernetes to remove the pod from the load balancer.


Writing a Custom HealthIndicator

You can implement HealthIndicator to monitor your own critical dependencies (such as an external payment provider, search index, or local file buffer):

java
package com.example.orderservice.actuator;

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

import java.net.HttpURLConnection;
import java.net.URL;

@Component
public class ExternalPaymentGatewayHealthIndicator implements HealthIndicator {

    private static final String PAYMENT_GATEWAY_URL = "https://api.paymentprovider.example/health";

    @Override
    public Health health() {
        try {
            long latencyStart = System.currentTimeMillis();
            boolean reachable = pingService(PAYMENT_GATEWAY_URL);
            long latency = System.currentTimeMillis() - latencyStart;

            if (reachable) {
                return Health.up()
                    .withDetail("service", "External Payment Gateway")
                    .withDetail("latencyMs", latency)
                    .withDetail("endpoint", PAYMENT_GATEWAY_URL)
                    .build();
            } else {
                return Health.down()
                    .withDetail("service", "External Payment Gateway")
                    .withDetail("error", "Received non-200 response code")
                    .build();
            }
        } catch (Exception e) {
            return Health.down(e)
                .withDetail("service", "External Payment Gateway")
                .withDetail("error", e.getMessage())
                .build();
        }
    }

    private boolean pingService(String urlStr) throws Exception {
        URL url = new URL(urlStr);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setConnectTimeout(2000);
        connection.setReadTimeout(2000);
        connection.setRequestMethod("GET");
        int responseCode = connection.getResponseCode();
        return responseCode == 200;
    }
}

Now, /actuator/health includes an externalPaymentGateway entry with your live latency numbers.


Runtime Log Level Modification via /actuator/loggers

One of Actuator's most powerful operational capabilities is mutating logging levels on the fly without restarting the JVM.

Suppose your application runs in production with root log level INFO. A customer reports an intermittent order processing bug in package com.example.orderservice.service.

  1. Inspect current logging level:

    bash
    curl http://localhost:8080/actuator/loggers/com.example.orderservice.service

    Response:

    json
    {"configuredLevel": "INFO", "effectiveLevel": "INFO"}
  2. Temporarily switch level to DEBUG via an HTTP POST:

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

    The application immediately begins printing debug logs for that specific package.

  3. Once the issue is diagnosed, switch it back to INFO using the same endpoint. Zero server restarts, zero downtime.


Production Security for Actuator

Exposing sensitive endpoints like /actuator/env or /actuator/threaddump publicly on the internet is a severe security vulnerability that leaks passwords, database URLs, and internal code structures.

Production Best Practices:

  1. Run Actuator on a Dedicated Management Port:

    properties
    # Application serves public customer traffic on port 8080
    server.port=8080
    
    # Management endpoints serve on private internal port 9090
    management.server.port=9090

    Network firewalls block external traffic to port 9090, exposing it strictly to internal monitoring tools inside your private virtual cloud.

  2. Secure with Spring Security: Require administrator credentials for sensitive actuator routes:

    java
    @Bean
    public SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            .securityMatcher(EndpointRequest.toAnyEndpoint())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class)).permitAll()
                .anyRequest().hasRole("ADMIN")
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

Interview Questions & Pitfalls

Q1: What is the primary purpose of Spring Boot Actuator?

Spring Boot Actuator provides production ready operational features out of the box, including health checks, JVM and system metrics collection, environment property inspection, thread dumps, and runtime configuration auditing. It allows operations teams and orchestration platforms (Kubernetes, Prometheus) to monitor and manage applications live.

Q2: Which actuator endpoint is enabled and exposed over HTTP by default in Spring Boot?

Only the /actuator/health endpoint is exposed over HTTP by default. All other endpoints are disabled or hidden by default to protect system security until explicitly included via management.endpoints.web.exposure.include.

Q3: How do you create a custom health check in Spring Boot?

Create a Spring @Component that implements the HealthIndicator interface and overrides the health() method. Inside the method, execute your custom verification logic and return Health.up().build() or Health.down().withDetail("reason", ...).build().

Q4: What is the security danger of setting management.endpoints.web.exposure.include=* in production?

Setting include=* exposes sensitive endpoints such as /actuator/env (which contains environment variables and configuration secrets), /actuator/beans (which exposes internal application architecture), and /actuator/threaddump to anyone who can reach the port. This leaks confidential infrastructure information and credentials.

Q5: How does Kubernetes use /actuator/health?

Kubernetes uses Actuator's health probes for container lifecycle management. The liveness probe (/actuator/health/liveness) verifies if the container is running; if it fails, Kubernetes restarts the pod. The readiness probe (/actuator/health/readiness) verifies if the container is ready to accept user traffic; if it fails, Kubernetes stops routing incoming requests to that pod until it recovers.