Spring Boot Actuator

This presentation covers the features and usage of Spring Boot Actuator.

Enabling Production-ready Features

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

access: /actuator endpoint

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/actuator",
      "templated": false
    },
    "health-path": {
      "href": "http://localhost:8080/actuator/health/{*path}",
      "templated": true
    },
    "health": {
      "href": "http://localhost:8080/actuator/health",
      "templated": false
    }
  }
}

Endpoints

management:
  endpoints:
    web:
      exposure:
        include: "*"

Actuator Rest API

access control

Only expose what you really need!

By default, access to all endpoints except for shutdown and heapdump is unrestricted.

management:
  endpoint:
    shutdown:
      access: unrestricted
    heapdump:
      access: unrestricted

Security

Additional port for internal access

management:
  server:
    port: 8081
http :8081/actuator/health

Add Spring Security

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>
management:
  endpoint:
    env:
      show-values: when-authorized
spring:
  security:
    user:
      name: admin
      password: 123456

Stricter - Add Custom SecurityFilterChain

	@Bean
	SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher(EndpointRequest.toAnyEndpoint())
			.authorizeHttpRequests(requests -> requests
				.requestMatchers(EndpointRequest.to("health", "info", "metrics", "prometheus")).permitAll()
				.anyRequest().authenticated()
			)
			.httpBasic(Customizer.withDefaults())
			.csrf(csrf -> csrf.disable());
		return http.build();
	}

Health

Show Details

management:
  endpoint:
    health:
      show-details: always

http :8081/actuator/health

{
    "components": {
        "diskSpace": {
            "details": {
                "exists": true,
                "free": 870792753152,
                "path": "/Users/matthew/projects/meirongdev/actuator-demo/.",
                "threshold": 10485760,
                "total": 994662584320
            },
            "status": "UP"
        },
        "ping": {
            "status": "UP"
        },
        "ssl": {
            "details": {
                "invalidChains": [],
                "validChains": []
            },
            "status": "UP"
        }
    },
    "status": "UP"
}

More builtin

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Additional starters may add their own health indicators

output when redis is not available:

{
    "components": {
        "diskSpace": {
            ...
            "status": "UP"
        },
        "ping": {
            "status": "UP"
        },
        "redis": {
            "details": {
                "error": "org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis"
            },
            "status": "DOWN"
        },
        "ssl": {
            "details": {
                "invalidChains": [],
                "validChains": []
            },
            "status": "UP"
        }
    },
    "status": "DOWN"
}

Customize Health Indicator

@Bean(name = "depsIndicator")
    HealthIndicator depsHealthIndicator() {
        return () -> {
            Health.Builder healthBuilder = Health.up();

            // Define the services to check
            Map<String, Boolean> serviceHealthMap = Map.of(
                "serviceA", checkServiceA(),
                "serviceB", checkServiceB()
            );

            // Check each service and update health status
            serviceHealthMap.entrySet().forEach(entry -> {
                if (!entry.getValue()) {
                    healthBuilder.down().withDetail(entry.getKey(), entry.getKey() + " is down");
                } else {
                    healthBuilder.withDetail(entry.getKey(), entry.getKey() + " is healthy");
                }
            });

            boolean allServicesHealthy = serviceHealthMap.values().stream()
                .allMatch(Boolean::booleanValue);

            return allServicesHealthy ? healthBuilder.build() : healthBuilder.down().build();
        };
    }

output in /actuator/health:

    "depsIndicator": {
            "details": {
                "serviceA": "serviceA is healthy",
                "serviceB": "serviceB is down"
            },
            "status": "DOWN"
        },

Cache

management:
    endpoint:
        health:
            cache:
                time-to-live: 30s

Status Aggregation

management:
  endpoint:
    health:
      status:
        order: down, out-of-service, unknown, up
        http-mapping:
          down: 503
          out-of-service: 503
          unknown: 500
          up: 200

Add custom http-mapping will remove default mapping.

K8s support

management:
  endpoint:
    health:
      probes:
        enabled: true # This is enabled by default in k8s environment
        add-additional-paths: true
       group:
         liveness:
           include: diskSpace, ping
         readiness:
           include: db, redis # include all the indicators you want

Spring Boot auto-detects Kubernetes deployment environments by checking the environment for “_SERVICE_HOST” and “_SERVICE_PORT” variables.

You can override this detection with the spring.main.cloud-platform configuration property.

In the k8s side, can configure like this:

livenessProbe:
  httpGet:
    path: "/actuator/health/liveness"
    port: <actuator-port>
  failureThreshold: ...
  periodSeconds: ...

readinessProbe:
  httpGet:
    path: "/actuator/health/readiness"
    port: <actuator-port>
  failureThreshold: ...
  periodSeconds: ...

For the graceful shutdown, can refer to Spring Boot 3 Graceful Shutdown with Kubernetes.

Info

Builtin contributors

  • build
  • env
  • git
  • java
  • os
  • process
  • ssl

Build Info

<plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 <goals>
 <goal>build-info</goal>
 </goals>
</plugin>
<plugin>
 <groupId>io.github.git-commit-id</groupId>
 <artifactId>git-commit-id-maven-plugin</artifactId>
</plugin>
{
    "git": {
        "branch": "main",
        "commit": {
            "id": "f08b993",
            "time": "2025-10-27T14:33:29Z"
        }
    }
}

Other Builtin Contributors

management:
  info:
    build:
      enabled: true
    git:
      enabled: true
    os:
      enabled: true
    process:
      enabled: true
    java:
      enabled: true
    ssl:
      enabled: true

Write your own TBC

@Bean
InfoContributor barInfoContributor() {
  return new InfoContributor() {
    @Override
    public void contribute(Info.Builder builder) {
      builder.withDetail("bar", Map.of("now", Instant.now().toString()));
    }
  };
}

Takeaways