Mastering Spring Boot 3 Actuator — Observability, Health Monitoring & Production Ops (Ultimate Guide)

Mastering Spring Boot 3 Actuator — Observability, Health Monitoring & Production Ops (Ultimate Guide)

In a cloud-native world, simply “deploying a Spring Boot app” is not enough. You need to know: Is it healthy? Is it fast? Is it failing silently? Can it auto-heal?
That’s where Spring Boot 3 Actuator becomes your best friend for observability and production operations.

What you’ll get in this guide:
  • Deep dive into Spring Boot 3 Actuator concepts (for production, not just local dev)
  • All important endpoints: /health, /metrics, /prometheus, /loggers, etc.
  • Custom health checks, business metrics & percentiles with Micrometer
  • How to integrate Prometheus + Grafana for dashboards
  • How to expose secure Kubernetes liveness/readiness probes
  • Logging, tracing and ops best practices for real projects

1. Spring Boot Actuator in Spring Boot 3 — Mental Model

Actuator turns your application into an observable system. Instead of guessing what’s happening in production, you expose:

  • Health – Is the app and its dependencies (DB, Redis, APIs) up?
  • Metrics – How many requests? How slow? How many errors?
  • Info – Build, git, environment info for debugging.
  • Threads & logs – What is the JVM doing? What are we logging?
  • Probes – Signals for Kubernetes / orchestrators to restart or route traffic.

In Spring Boot 3, Actuator is tightly integrated with:

  • Micrometer 1.x (metrics facade)
  • Jakarta namespace (since Boot 3 moved from javax → jakarta)
  • Modern Java versions (17+), containers and cloud platforms
Think of Actuator as the “ops API” for your Spring Boot application.

2. Adding Actuator & Basic Setup

2.1. Maven Dependency

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

2.2. Basic Configuration

By default, only a few endpoints are enabled over HTTP. You explicitly choose what to expose:

# application.properties

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

# Show detailed health info (for internal environments only!)
management.endpoint.health.show-details=always

# Use a nicer base path (optional)
management.endpoints.web.base-path=/actuator
Endpoint URL (default base path) Purpose
health /actuator/health Overall application health + components
metrics /actuator/metrics All available metrics names
info /actuator/info Build/git/app info
prometheus /actuator/prometheus Prometheus scrape format (Micrometer)
loggers /actuator/loggers View/update logger levels at runtime
✅ In production, never do management.endpoints.web.exposure.include=*. Expose only what your monitoring and ops tools really need.

3. Health Checks — Making Your App “Observable” for Ops & Kubernetes

3.1. Built-in Health Indicators

Spring Boot auto-registers health indicators if their libraries are on the classpath, for example:

  • Database (DataSource health)
  • Redis
  • RabbitMQ / Kafka
  • Disk space

You can see them by calling:

GET /actuator/health
GET /actuator/health/db
GET /actuator/health/diskSpace

3.2. Creating a Custom HealthIndicator

Example: Check an external payment API your service depends on.

@Component
public class PaymentApiHealthIndicator implements HealthIndicator {

    private final RestTemplate restTemplate;

    public PaymentApiHealthIndicator(RestTemplateBuilder builder) {
        this.restTemplate = builder.setConnectTimeout(Duration.ofMillis(500))
                .setReadTimeout(Duration.ofMillis(500))
                .build();
    }

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

            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up()
                        .withDetail("endpoint", "payment-api")
                        .withDetail("statusCode", response.getStatusCode().value())
                        .build();
            }
            return Health.down()
                    .withDetail("endpoint", "payment-api")
                    .withDetail("statusCode", response.getStatusCode().value())
                    .build();
        } catch (Exception ex) {
            return Health.down(ex).withDetail("endpoint", "payment-api").build();
        }
    }
}

3.3. Grouping Health Indicators

Sometimes you want a “readiness” group that only includes critical dependencies.

# application.properties

management.endpoint.health.group.readiness.include=db,paymentApi,redis
management.endpoint.health.group.liveness.include=ping

Now you get:

GET /actuator/health/readiness
GET /actuator/health/liveness

4. Metrics with Micrometer — From JVM Stats to Business KPIs

Spring Boot 3 uses Micrometer under the hood. You get standard metrics out of the box:

  • jvm.memory.used, jvm.threads.live
  • http.server.requests (per endpoint, method, status)
  • system.cpu.usage
  • DataSource metrics, cache metrics, etc.

4.1. Inspecting Metrics

GET /actuator/metrics           # list metric names
GET /actuator/metrics/jvm.memory.used
GET /actuator/metrics/http.server.requests

4.2. Creating a Custom Counter

Example: Count completed orders.

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final Counter orderCreatedCounter;

    public OrderController(MeterRegistry meterRegistry) {
        this.orderCreatedCounter = Counter
                .builder("app.orders.created.count")
                .description("Number of successfully created orders")
                .tag("region", "apac")
                .register(meterRegistry);
    }

    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
        // ... business logic
        orderCreatedCounter.increment();
        return ResponseEntity.ok(new OrderResponse("CREATED"));
    }
}

4.3. Timer & Percentiles for Latency

For performance monitoring, Timer with percentiles is powerful:

@Service
public class PaymentService {

    private final Timer paymentTimer;

    public PaymentService(MeterRegistry registry) {
        this.paymentTimer = Timer.builder("app.payment.latency")
                .description("Payment processing latency")
                .publishPercentiles(0.5, 0.95, 0.99)
                .maximumExpectedValue(Duration.ofSeconds(5))
                .register(registry);
    }

    public PaymentResult process(PaymentRequest request) {
        return paymentTimer.record(() -> {
            // slow external call, DB operations...
            simulateWork();
            return new PaymentResult("OK");
        });
    }

    private void simulateWork() {
        try { Thread.sleep(200 + (long) (Math.random() * 300)); }
        catch (InterruptedException ignored) {}
    }
}

5. Exposing Metrics to Prometheus & Visualizing with Grafana

5.1. Add Prometheus Registry

<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

5.2. Enable the Prometheus Endpoint

# application.properties

management.endpoints.web.exposure.include=health,metrics,prometheus
management.endpoint.prometheus.enabled=true

Now Prometheus can scrape:

GET /actuator/prometheus

5.3. Prometheus Configuration Example

scrape_configs:
  - job_name: "spring-boot-app"
    metrics_path: "/actuator/prometheus"
    scrape_interval: 15s
    static_configs:
      - targets: ["my-app.prod.internal:8080"]

5.4. Grafana Dashboards

In Grafana, configure Prometheus as a data source and:

  • Import a generic Spring Boot / Micrometer dashboard
  • Build custom panels for app.orders.created.count or app.payment.latency
  • Create alerts on error rate, latency, or CPU usage
📊 Tip: Focus on 3–5 core metrics first: request rate, error rate, latency, JVM memory, and DB connection usage. Then add more if needed.

6. Securing Actuator Endpoints for Production

Actuator gives a lot of power — which also means attack surface if not secured.

6.1. Add Spring Security

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

6.2. Example Security Configuration

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                // Allow unauthenticated health & readiness for K8s
                .requestMatchers("/actuator/health/**").permitAll()
                // Everything else actuator-related requires ROLE_ADMIN
                .requestMatchers("/actuator/**").hasRole("ADMIN")
                // Your app endpoints
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .csrf(csrf -> csrf.disable()); // For APIs; adjust if using browser sessions

        return http.build();
    }
}

6.3. Disable Risky Endpoints

# Disable environment endpoint (shows env variables)
management.endpoint.env.enabled=false

# Disable beans endpoint in production if not needed
management.endpoint.beans.enabled=false
In a hardened environment, Actuator should not be exposed publicly. Put it behind VPN, private network, or API gateway + strict auth.

7. Kubernetes Probes with Spring Boot 3 Actuator

Kubernetes needs to know:

  • Is the app alive? (liveness)
  • Is it ready to receive traffic? (readiness)

7.1. Enable Probes in Spring

# application.properties

management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true

You now get:

  • /actuator/health/liveness
  • /actuator/health/readiness

7.2. Example K8s Deployment Snippet

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
🔁 If readiness goes DOWN (e.g., DB down), Kubernetes will stop sending traffic to that pod, but liveness can remain UP so the app can recover without a full restart.

8. Logs, Log Levels & Live Troubleshooting with Actuator

8.1. View Logger Configuration

GET /actuator/loggers          # list all loggers and levels
GET /actuator/loggers/com.example.service.PaymentService

8.2. Change Log Level at Runtime

POST /actuator/loggers/com.example.service.PaymentService
Content-Type: application/json

{
  "configuredLevel": "DEBUG"
}

Great for temporary debugging in production without redeploying.

8.3. Best Practices

  • Use INFO + WARN as defaults; DEBUG only when needed.
  • Log correlation IDs or trace IDs in every log line.
  • Ship logs to a central platform (ELK, Loki, Cloud Logging).

9. Distributed Tracing & OpenTelemetry (High-Level)

Actuator plays nicely with tracing libraries (OpenTelemetry, Brave, etc.). At a high level:

  1. Add OpenTelemetry dependencies / agents.
  2. Configure exporters (Jaeger, Zipkin, OTEL Collector).
  3. Propagate trace context via HTTP headers between services.
  4. Use Actuator metrics and logs with trace IDs for full observability.

This lets you answer: “Why is this request slow across 5 microservices?”


10. Production Checklist for Actuator & Observability

  • ✅ Actuator enabled, only required endpoints exposed.
  • ✅ Spring Security configured with roles / API tokens for /actuator/**.
  • ✅ Health groups configured (liveness, readiness, maybe external/critical).
  • ✅ Micrometer custom metrics added for key business events.
  • ✅ Metrics exported to Prometheus / APM; dashboards created in Grafana.
  • ✅ Liveness & readiness probes wired into Kubernetes.
  • ✅ Logs structured + centralized; trace IDs included.
  • ✅ Load test your app and validate metrics/alerts before real traffic.
🧠 Rule of thumb: If you can’t see it, you can’t operate it. Actuator is non-negotiable for serious Spring Boot deployments.

11. Summary & Next Steps

Spring Boot 3 Actuator gives you a complete observability foundation:

  • Health endpoints to keep your app auto-healing and resilient.
  • Metrics and Micrometer to track performance and business KPIs.
  • Secure ops endpoints for logs, info, and on-the-fly debugging.
  • Seamless integration with Prometheus, Grafana, Kubernetes and tracing tools.

If you combine this with proper security, resilience patterns (Resilience4j), caching, and microservices architecture, you get a truly production-ready system.

Next: wire this into your real project, and then layer in API gateway security, Redis caching, and resilience patterns (see related posts below). 🚀