Spring Boot Actuator — Avoid Connection Pool Exhaustion
Payment service latency spiked from 50ms to 15,000ms — HikariCP pool was the silent killer.
- Spring Boot Actuator exposes operational endpoints (/health, /metrics, /prometheus) that turn your running JVM from a black box into an observable system
- Health groups (liveness, readiness, startup) map directly to Kubernetes probe types — never put external dependency checks in liveness probes
- Micrometer is the metrics engine: Counters for totals, Gauges for current values, Timers for latency percentiles (p50/p95/p99)
- Prometheus scrapes /actuator/prometheus every 15s — using /actuator/health for scraping creates a thundering herd at scale
- Dynamic log level changes via /actuator/loggers turn a 30-minute 'add logging and redeploy' cycle into a 10-second API call
- management.metrics.tags.application=${spring.application.name} is the one line most teams forget — without it, metrics from different services collide in Prometheus
- The full stack is Actuator (sensors) + Prometheus (recorder) + Grafana (visualizer) + Alertmanager (alerter) — Actuator alone gives you endpoints, not observability
In the world of microservices, 'it works on my machine' is not enough. You need to know if it works in production at 3:00 AM under peak load. Spring Boot Actuator is the industry-standard framework that transforms your dark application into an observable system by exposing HTTP and JMX endpoints that reveal the inner state of your running JVM.
I learned this the hard way. In 2021, our team deployed a Spring Boot payment service to production. It ran fine for three weeks. Then one Saturday at 2 AM, latency spiked to 15 seconds per request. We had no metrics, no health checks beyond a basic /ping, and no idea what was wrong. It took four hours to diagnose a connection pool exhaustion issue that a single Micrometer gauge would have caught in 30 seconds. That incident cost us $40,000 in lost transactions and a very uncomfortable Monday morning post-mortem.
After that, we instrumented everything. Every service got Actuator, Micrometer, Prometheus, and Grafana before it ever touched production. We have not had a mystery outage since.
This guide moves beyond basic dependency injection and covers the operational side of Java development. We will explore how to monitor application health, track custom business metrics, expose build traceability info, manage log levels dynamically, secure sensitive endpoints, wire everything into a Prometheus/Grafana stack, and build custom endpoints for operational control — all with the application.yml configuration you can actually copy into a project.
The Three Pillars of Observability
Before diving into Actuator, understand the framework that every production monitoring system is built on. Observability rests on three pillars, and knowing which pillar answers which question is the difference between a 4-hour incident and a 10-minute resolution.
- Metrics — Numeric measurements over time. Request rate, error rate, latency percentiles, JVM heap usage, connection pool saturation. These are what Prometheus scrapes and Grafana displays. Metrics answer 'how much' and 'how fast' and 'how often.' They are the first signal that something is wrong.
- Logs — Discrete events with context. A log entry says 'order #4521 failed with NullPointerException at PaymentService.java:87 for user abc123.' Logs answer 'what happened' and 'why.' Spring Boot's structured logging in JSON format feeds into ELK, Loki, or Datadog. They answer the question that the metric alert raised.
- Traces — A request's journey across services. In a microservice architecture, a single user action might touch 8 services. A trace connects those dots — showing that the 2-second delay happened in the inventory service on the third hop, not in the API gateway. Spring Boot integrates with Micrometer Tracing and OpenTelemetry for distributed tracing.
Spring Boot Actuator primarily addresses the metrics and health pillars. But a production-grade observability stack needs all three working together. The order of implementation matters more than most teams realize.
The Anatomy of Actuator: Observability in Action
Spring Boot Actuator exists because manual health checks are a recipe for failure. Instead of writing custom endpoints to check if your database is alive or if your disk space is full, Actuator provides these out of the box. By adding the starter dependency and configuring application.yml, you instantly gain access to the /actuator base path with standardized operational endpoints.
However, most endpoints are hidden by default for security. The real power lies in the /health and /prometheus endpoints. One critical detail that trips up almost every team the first time: the /actuator/prometheus endpoint does not exist unless micrometer-registry-prometheus is on the classpath. The actuator starter and the Prometheus registry are separate dependencies. Adding spring-boot-starter-actuator without micrometer-registry-prometheus gives you health and info but not Prometheus metrics.
One thing worth saying explicitly: Actuator endpoints are not your application REST API. They are operational endpoints meant for internal monitoring infrastructure. Your security model should reflect this — your monitoring stack gets access through specific roles, your customers never touch these endpoints.
Custom Metrics with Micrometer — Beyond Health Checks
Health checks tell you if the system is alive. Metrics tell you how it is performing. Spring Boot uses Micrometer, a dimensional metrics instrumentation facade — think SLF4J but for metrics. It lets you track business-relevant things like order count, payment latency percentiles, and cart abandonment rate rather than just JVM garbage collection statistics.
Micrometer supports several meter types. Choosing the right one matters:
- Counter — Monotonically increasing value. Use for total requests, total errors, and orders placed. Never goes down. Query with
rate()in Prometheus to get events per second. - Gauge — A value that fluctuates. Use for current queue depth, active connections, and temperature. You report the current value; Micrometer samples it on each scrape.
- Timer — Measures duration and rate simultaneously. Use for request latency, database query time, and external API call duration. Gives you percentiles (p50, p95, p99) automatically.
- Distribution Summary — Like a Timer but for arbitrary values, not time. Use for payload sizes, batch sizes, and record counts.
The @Timed annotation is the fastest way to instrument a controller method. Spring Boot auto-configures a TimedAspect bean when Micrometer is on the classpath — you annotate the method and Micrometer handles the rest. For custom business logic, inject MeterRegistry directly and build meters explicitly.
Custom Actuator Endpoints — Operational Control Beyond Health
The built-in Actuator endpoints cover most operational needs. But there are legitimate cases where you need operational endpoints specific to your application: runtime feature flag status, current circuit breaker state, deployment metadata that goes beyond what /actuator/info provides, or a cache invalidation trigger that ops can call without a full deploy.
Spring Boot makes this straightforward with the @Endpoint annotation. Annotate a Spring component with @Endpoint(id='yourEndpoint') and methods with @ReadOperation (HTTP GET), @WriteOperation (HTTP POST), or @DeleteOperation (HTTP DELETE). Spring Boot automatically exposes the endpoint at /actuator/yourEndpoint and applies the same security model as built-in endpoints — whatever your SecurityFilterChain says applies here too.
The design rule: @ReadOperation methods should have no side effects. @WriteOperation methods change state and must be secured with ADMIN role — they are essentially a surgical tool for ops teams, not a general API. I have seen @WriteOperation endpoints used to toggle circuit breakers, clear Redis caches, and reload configuration from external sources — all without a redeploy.
Securing Actuator Endpoints with Spring Security
By default in Spring Boot 2.x and later, Actuator endpoints sit behind Spring Security if it is on the classpath. But behind security does not mean secure. The default configuration often allows all authenticated users to access all endpoints — including ones that dump environment variables, heap contents, and thread states.
The production pattern: restrict Actuator endpoints to a dedicated monitoring role or internal network. Your Prometheus scraper authenticates with a service account. Your developers get read-only access to health and info. Nobody outside the internal network touches env or heapdump. The 15-line SecurityFilterChain in this section has prevented two credential exfiltration incidents on teams I have worked with.
One detail most guides miss: when you add a dedicated SecurityFilterChain for /actuator/**, you need @Order(1) to give it higher priority than your application's main SecurityFilterChain. Without the ordering, Spring applies your main chain first, which may have different rules than what you intend for Actuator.
Prometheus and Grafana Integration — Full Stack Setup
Actuator exposes the /actuator/prometheus endpoint in Prometheus exposition format — a text-based, human-readable format that Prometheus understands natively. But Prometheus needs to be told where to scrape. This is where most tutorials end and most teams get stuck.
On the Spring Boot side: add micrometer-registry-prometheus to your dependencies and management.endpoints.web.exposure.include=prometheus to your application.yml. That is it. The endpoint auto-configures.
On the Prometheus side: add a scrape_config block pointing at your application. The two details most engineers get wrong: using /actuator/health as the metrics_path instead of /actuator/prometheus (heavy versus lightweight), and using static_configs in Kubernetes where pods get new IPs on every restart.
On the Grafana side: import dashboard ID 4701 (JVM Micrometer) from grafana.com/dashboards for a production-ready Spring Boot dashboard. It covers heap usage, GC pause time, HTTP request rates, and database connection pool saturation out of the box — no PromQL required to get started.
Kubernetes Probes — Liveness, Readiness, and Startup
If you are deploying Spring Boot to Kubernetes, Actuator's health groups become the backbone of your pod lifecycle management. Misconfiguring these probes is the single most common cause of cascading restarts in Spring Boot Kubernetes deployments — and it is entirely preventable.
Spring Boot 2.3 introduced health groups — separate health endpoints for different probe types. This is critical because liveness and readiness must check different things:
- Liveness ('Is the app alive?'): Checks only internal JVM state. If this fails, Kubernetes restarts the pod. Keep it lightweight — no database calls, no external API calls. A temporary DB blip should never restart your pods.
- Readiness ('Can the app accept traffic?'): Checks dependencies — database reachable, cache warm, broker connected. If this fails, Kubernetes removes the pod from the load balancer but does not restart it. This is the correct behavior for a transient dependency failure.
- Startup ('Has the app finished booting?'): For slow-starting apps. Kubernetes waits for this to pass before running liveness and readiness probes. The math matters: failureThreshold × periodSeconds = maximum startup window. A Spring Boot app with 60 seconds of startup time needs failureThreshold: 12 with periodSeconds: 10 for a 120-second safety window — always give at least 2x your measured startup time.
I once debugged a 20-replica production deployment restarting every 90 seconds. The liveness probe was checking database connectivity. A 30-second DB connection spike triggered liveness failures across all 20 pods simultaneously. They all restarted, reconnected at once, overwhelmed the DB, the DB connection time spiked again, and the cycle repeated. The fix was one line in the Kubernetes deployment YAML: change the liveness probe path from /actuator/health to /actuator/health/liveness.
The /actuator/info Endpoint — Deployment Traceability
The /actuator/info endpoint is the most underused feature in the Actuator suite. It lets you expose build information — Git commit hash, build timestamp, artifact version — directly from your running application. When something breaks in production, the first question is always 'what version is running?' Without this endpoint configured, answering that question means digging through CI/CD pipeline logs, which takes minutes you do not have during an active incident.
The setup requires two Maven plugins: the spring-boot-maven-plugin with the build-info goal, and the git-commit-id-plugin to embed Git metadata. Once configured, every build automatically embeds its own DNA into the artifact. Every deployment automatically reports exactly what code is running.
In production setups, every Grafana dashboard should have a deployment panel that queries /actuator/info across instances. If instances report different commit hashes during a rolling deploy, you can see the split-brain state in real time. If a rollback happened silently, it shows up immediately in this panel.
Dynamic Log Level Management — Debug Without Redeploying
One of Actuator's most powerful day-two features: changing log levels at runtime without restarting the application. Need to enable DEBUG logging for a specific package to troubleshoot a production issue? Hit /actuator/loggers, change the level, reproduce the issue, read the logs, then change it back. No restart. No downtime. No redeploy cycle.
This converts 'we need to add more logging and redeploy' — a 30-minute process minimum in most CI/CD pipelines — into a 10-second API call. During incident response, this is the difference between a 10-minute resolution and a 45-minute resolution while customers are actively impacted.
The endpoint supports GET to read current levels and POST to change them. Spring Security should restrict POST to ADMIN role — TRACE logging in production can generate gigabytes of log data per minute, fill disk, and cascade into other failures. Always reset the log level after you have captured what you need. Build that reset command into your debugging runbook as a mandatory step.
For Hibernate SQL debugging specifically, enable TRACE on org.hibernate.SQL to see the exact SQL being generated and org.hibernate.type.descriptor.sql to see the actual bind parameter values. This combination has diagnosed more mysterious data bugs than any other technique I know.
Micrometer Integration and Docker Deployment
To make this production-ready, you containerize the application with a focus on how Docker handles the health signal from Actuator and how the JVM is tuned for container environments.
The Docker HEALTHCHECK instruction tells the Docker daemon whether the container is healthy. By pointing it at /actuator/health/liveness, you get the same lightweight JVM-only health logic that Kubernetes uses at the liveness probe level. This matters most for Docker Compose deployments, which do not have Kubernetes probe support.
Two things to watch for in every containerization. First, the HEALTHCHECK command needs wget or curl to be present in the base image. Distroless images include neither — you will need to switch to a slim base image or remove Docker HEALTHCHECK and rely exclusively on Kubernetes probes. Second, the JVM memory flags: without -XX:+UseContainerSupport, older JDK versions read the host machine's memory rather than the container's memory limits and allocate too large a heap, causing OOM kills. Modern JDK 17 and later handle this automatically, but the flag is harmless and makes the intent explicit.
| Monitoring Aspect | Legacy Approach (Manual) | Modern Approach (Actuator) |
|---|---|---|
| Health Checks | Custom /status endpoints with inconsistent JSON structure. Each developer implements their own format. You get 'OK' or nothing. No component-level detail, no Kubernetes probe integration. | Standardized /health endpoint with nested component status, auto-aggregation (any DOWN component makes overall status DOWN), and health groups (liveness, readiness) that map directly to Kubernetes probe types. |
| Metrics Gathering | Log parsing, manual JMX MBean registration, or custom counters in Redis. Fragile, hard to query, impossible to aggregate across instances, and requires per-service implementation. | Micrometer integration with dimensional metrics. Auto-instrumentation of JVM, HTTP requests, and HikariCP. One dependency addition exports everything to Prometheus, Datadog, InfluxDB, or New Relic. |
| Runtime Management | Requires application restart to change log levels. 'Add a log statement and redeploy' is a 30-minute process with a deployment window approval. Debugging in production is a full release cycle. | Dynamic log level updates via /actuator/loggers — change any package's log level in 10 seconds without restarting. View environment variables and system properties via /actuator/env (secured). |
| Security | Ad-hoc security filters, often left unprotected or protected inconsistently. Actuator endpoints exposed with default Spring Security or no security at all — common source of credential exfiltration. | Integrated with Spring Security. Fine-grained access control per endpoint using @Order SecurityFilterChain. Role-based restrictions. HTTP Basic for Prometheus scraper. CSRF disabled for stateless endpoints. |
| Deployment Traceability | Check CI/CD logs, SSH into the server, run git log. No programmatic way to verify which code version is running. Ghost deployments go undetected for hours during incidents. | /actuator/info exposes Git commit hash, build timestamp, and artifact version from the running process. CI/CD post-deploy verification catches ghost deployments in 2 seconds. |
| Operational Control | Any operational action (cache clear, config reload, feature toggle) requires a code change, pull request, code review, build, and deploy. A 30-minute minimum for a surgical change. | Custom @Endpoint with @WriteOperation provides surgical operational control without a deploy. Cache invalidation, circuit breaker toggles, and config reloads become API calls restricted to ADMIN role. |
Key Takeaways
- Actuator is the bridge between application code and operational visibility — it is non-negotiable for any Spring Boot service running in production.
- Use readiness probes to control load balancer traffic and liveness probes to signal Kubernetes when a pod needs a restart. Never put external dependency checks in liveness probes — the cascading restart pattern it creates has taken down production systems at companies with mature engineering teams.
- Micrometer is the metrics engine. Use Counters for total counts, Gauges for current values, and Timers for latency percentiles. Mean latency is a liar — always instrument and alert on p95 or p99.
- The /actuator/info endpoint is the fastest way to answer 'what version is running?' during an incident. Configure it once with git-commit-id-plugin and build-info goal. Add post-deploy verification to your CI/CD pipeline that compares the running commit hash against what was just deployed.
- Dynamic log level management via /actuator/loggers turns a 30-minute debugging deployment cycle into a 10-second API call. Build the reset command into your runbook as a mandatory step — TRACE logging left running fills disks.
- Always secure Actuator endpoints with a dedicated SecurityFilterChain using @Order(1). Whitelist only what you need. env, heapdump, and threaddump are the three most dangerous — restrict to ADMIN role and consider never exposing them at all.
- Add management.metrics.tags.application=${spring.application.name} to every service's application.yml — it is the one line most teams forget. Without it, metrics from different services are indistinguishable in Prometheus.
- Never create high-cardinality metrics with dynamic tag values like userId or requestId. Low-cardinality dimensions only: service name, region, HTTP method, status code, error type.
- The observability stack is Actuator (sensors) + Prometheus (recorder) + Grafana (visualizer) + Alertmanager (alerter). All four work together — Actuator alone gives you endpoints, not observability.
Common Mistakes to Avoid
- Exposing all endpoints via management.endpoints.web.exposure.include=* in production
Symptom: Automated scanners find /actuator/env within hours. AWS credentials, database passwords, and API keys are returned in JSON plaintext. /actuator/heapdump dumps all in-memory data including user sessions and PII.
Fix: Whitelist only what your monitoring stack needs: health,info,prometheus,loggers. Explicitly exclude heapdump,env,threaddump,shutdown. Add a SecurityFilterChain with @Order(1) and role-based access control. - Missing micrometer-registry-prometheus dependency — /actuator/prometheus returns 404
Symptom: /actuator/prometheus is listed in exposure.include but returns 404. Prometheus shows the target as having no metrics. Teams spend hours checking security configuration before realizing the dependency is missing.
Fix: Add io.micrometer:micrometer-registry-prometheus to pom.xml or build.gradle. This is separate from spring-boot-starter-actuator. Without it, the /actuator/prometheus endpoint does not exist regardless of what exposure.include contains. - Performing heavy, blocking I/O inside a Custom Health Indicator
Symptom: Health checks take 2 to 5 seconds because they call external APIs synchronously. Kubernetes probes timeout. Prometheus marks the target as down. CPU spikes every 10 seconds when probes fire simultaneously across all instances.
Fix: Keep health indicators under 200ms. Use cached results with periodic background refresh via a scheduled task instead of live calls on every probe. Set connectTimeout and readTimeout to 2000ms maximum in your health indicator HTTP client. - Leaving management.endpoint.health.show-details set to 'always'
Symptom: Anyone who hits /actuator/health sees database versions, connection pool sizes, external API response times, and disk usage. Attackers use this to fingerprint infrastructure and identify known CVEs for specific database or framework versions.
Fix: Set show-details to when_authorized or never in production. Only MONITORING or ADMIN roles should see component-level health details. Kubernetes probes only need the status field — they do not need details. - Using /actuator/health as the Kubernetes liveness probe instead of /actuator/health/liveness
Symptom: A transient database blip causes the full health check to fail. Kubernetes restarts every pod simultaneously. They all reconnect at once, creating a connection storm that worsens the original blip into a cascading restart loop.
Fix: Use /actuator/health/liveness for liveness probes — it checks only JVM internal state. Move dependency checks to /actuator/health/readiness. Enable health groups with management.endpoint.health.probes.enabled=true. - Not setting management.metrics.tags.application globally
Symptom: Prometheus shows http_server_requests_seconds from five different services all mixed together. Grafana dashboards cannot filter by service. Queries return combined meaningless numbers across the entire fleet.
Fix: Set management.metrics.tags.application=${spring.application.name} in every service's application.yml. Every metric emitted gets this tag automatically. Prometheus can then filter by application label in every query. - Creating high-cardinality metrics by using dynamic values as tags
Symptom: A Counter tagged with userId creates one unique time series per user. With 100,000 users, Prometheus runs out of memory. Scrape failures increase. TSDB storage fills up in days. Prometheus restarts frequently.
Fix: Keep tags to low-cardinality dimensions: service name, region, HTTP method, HTTP status code, error type. Never use request IDs, user IDs, or timestamps as tag values. User-level detail belongs in log aggregation, not metrics. - Ignoring the /actuator/info endpoint entirely
Symptom: During a production incident the team cannot answer 'what version is running?' without checking CI/CD logs. Ghost deployments — containers running old code despite a green pipeline — go undetected for hours.
Fix: Configure spring-boot-maven-plugin build-info goal and git-commit-id-plugin in pom.xml. Add a post-deploy verification step in CI/CD that curls /actuator/info and compares git.commit.id against the deployed commit hash.
Interview Questions on This Topic
- QWhat is the difference between 'Liveness' and 'Readiness' probes in the context of Spring Boot 2.3+ Actuator? What should each probe check, and what happens when each one fails in Kubernetes?Mid-levelReveal
- QHow would you implement a custom metric to track the number of successful vs failed login attempts using Micrometer's MeterRegistry? What meter type would you use and why?Mid-levelReveal
- QIn a high-security environment, how do you restrict access to the /actuator/prometheus endpoint to only the Prometheus scraper's service account?SeniorReveal
- QExplain the 'Thundering Herd' problem that can occur if monitoring systems scrape /actuator/health simultaneously. How would you mitigate it?SeniorReveal
- QHow does the @WriteOperation annotation work in a custom @Endpoint, and what are the safety implications compared to a @ReadOperation? When would you create a custom endpoint?SeniorReveal
- QWhat is the difference between a Counter, Gauge, and Timer in Micrometer? Give a real-world example of when you would use each one in a payment processing service.Mid-levelReveal
- QHow would you use the /actuator/loggers endpoint to troubleshoot a production issue without restarting the application? Walk through the exact steps.Mid-levelReveal
- QExplain how the /actuator/info endpoint can be enriched with Git commit information. What Maven plugins are required, and how would you use this in a CI/CD verification step?Mid-levelReveal
- QWhat is the risk of setting management.endpoints.web.exposure.include=* in production? What specific endpoints are the most dangerous and why?Mid-levelReveal
Frequently Asked Questions
What is the difference between /actuator/health, /actuator/health/liveness, and /actuator/health/readiness?
The base /actuator/health endpoint returns the aggregated status of all health indicators — database connectivity, disk space, external APIs, message brokers, and any custom indicators. It is the full-picture health check.
Spring Boot 2.3 introduced health groups: /actuator/health/liveness checks only whether the JVM is responsive with no external dependency checks, and /actuator/health/readiness checks whether the application can serve traffic and includes dependency checks.
In Kubernetes, use liveness for the liveness probe — if it fails, Kubernetes restarts the pod. Use readiness for the readiness probe — if it fails, the pod is removed from the load balancer but not restarted. Never use the full /actuator/health for liveness probes — a temporary database blip would restart every pod simultaneously.
Requires management.endpoint.health.probes.enabled=true in application.yml — without this, the liveness and readiness paths return 404.
How do I create a custom Actuator endpoint beyond health checks?
Annotate a Spring component with @Endpoint(id='yourEndpoint') and annotate methods with @ReadOperation (GET), @WriteOperation (POST), or @DeleteOperation (DELETE). Spring Boot automatically exposes the endpoint at /actuator/yourEndpoint.
For web-only endpoints with no JMX exposure, use @WebEndpoint instead of @Endpoint.
Custom endpoints inherit the same security model as built-in ones — they appear in your exposure.include list and respect the SecurityFilterChain you define. Secure @WriteOperation methods with ADMIN role since they change production state. Common use cases: deployment metadata richer than /actuator/info, runtime feature flag management, cache invalidation triggers, and circuit breaker state display and control.
How does Prometheus scrape Spring Boot Actuator metrics?
Prometheus pulls metrics from your application's /actuator/prometheus endpoint at a configured interval (default 15 seconds). On the Spring Boot side: add micrometer-registry-prometheus dependency and include prometheus in management.endpoints.web.exposure.include — this auto-configures the endpoint. On the Prometheus side: add a scrape_config block with metrics_path: '/actuator/prometheus' and your application's host and port.
For Kubernetes, use kubernetes_sd_configs with pod annotations instead of static targets. Pods annotated with prometheus.io/scrape: 'true' are auto-discovered. Each scrape returns all current metric values in Prometheus exposition text format. Prometheus stores these as time series in its TSDB.
Common issue: /actuator/prometheus returns 404 despite being in exposure.include — check that micrometer-registry-prometheus is in your pom.xml. It is a separate dependency from spring-boot-starter-actuator.
Can I change log levels in a running Spring Boot application without restarting?
Yes. POST to /actuator/loggers/{package.name} with a JSON body of {"configuredLevel": "DEBUG"}. The change takes effect immediately — no restart, no redeploy. To reset to default, POST with {"configuredLevel": null}. Null means inherit from the parent logger — this is different from explicitly setting INFO, which overrides any future configuration changes.
For Hibernate SQL debugging: enable TRACE on org.hibernate.SQL for query text and org.hibernate.type.descriptor.sql for bind parameter values.
Always secure POST with Spring Security restricting it to ADMIN role — TRACE logging generates gigabytes of output per minute and can fill disk quickly. Build the reset command into your incident runbook as a mandatory step, not an optional one.
What information does the /actuator/info endpoint show, and how do I populate it?
By default /actuator/info returns an empty JSON object — it must be explicitly populated. Two sources of data:
Build metadata: add the spring-boot-maven-plugin's build-info execution goal. This generates build-info.properties at compile time containing artifact name, version, and build timestamp. Appears under the build key in the response.
Git metadata: add git-commit-id-plugin to your Maven build. This embeds the commit hash (abbreviated), branch name, and commit time into git.properties at build time. Appears under the git key.
You can also add custom info via application.properties: info.app.description=Order processing service. These appear under the app key.
Use this in CI/CD post-deploy verification: compare git.commit.id from the running instance against the commit hash your pipeline just built to catch ghost deployments.
How do I secure Actuator endpoints in production?
Create a dedicated SecurityFilterChain with @Order(1) that matches /actuator/**. The @Order(1) ensures this chain has higher priority than your main application chain.
Permit /actuator/health/** and /actuator/info — needed by load balancers and Kubernetes probes without authentication. Restrict /actuator/prometheus to MONITORING role with HTTP Basic authentication. Restrict /actuator/loggers, /actuator/env, /actuator/heapdump, and /actuator/threaddump to ADMIN role. Deny everything else.
Also set management.endpoints.web.exposure.include to a whitelist — health, info, prometheus, loggers — and never use wildcard. Disable CSRF for the actuator security chain since these endpoints are called by automated systems, not browsers. Set management.endpoint.health.show-details to when_authorized or never in application.yml.
What is the difference between a Counter, Gauge, and Timer in Micrometer?
A Counter tracks a monotonically increasing value — it only goes up. Use it for total requests, total errors, and orders placed. Query with rate(counter_total[5m]) in Prometheus to get events per second. A Counter is wrong for any value that can decrease.
A Gauge tracks a value that goes up and down. Use it for current queue depth, active connections, and heap usage. You report the current value on demand and Micrometer samples it on each Prometheus scrape. A Gauge is wrong for cumulative counts.
A Timer measures duration and count simultaneously. Use it for request latency, database query time, and payment processing duration. It automatically calculates count, total time, and percentiles. Add publishPercentiles(0.5, 0.95, 0.99) to expose p50, p95, and p99 as Prometheus labels for alerting.
Choose the wrong type and you miss the signal: a Gauge for total orders loses the rate information, a Counter for connection pool size loses the current value, a Timer without percentiles hides the tail latency where the real problems live.
How do I handle Actuator in a Kubernetes environment with multiple replicas?
Configure all three probe types: startupProbe using /actuator/health/liveness with failureThreshold × periodSeconds set to at least 2× your measured startup time. livenessProbe using /actuator/health/liveness with no external dependency checks — JVM state only. readinessProbe using /actuator/health/readiness with dependency checks.
Enable health groups in application.yml with management.endpoint.health.probes.enabled=true — without this, the liveness and readiness paths return 404.
For metrics scraping, use Prometheus kubernetes_sd_configs with pod annotations rather than static targets. Pods get new IPs on every restart — static targets break on every rolling deploy. Add prometheus.io/scrape: 'true' and prometheus.io/port: '8080' annotations to your pod spec.
Set management.metrics.tags.application=${spring.application.name} so metrics from different service replicas are identifiable in Prometheus and can be filtered or aggregated by service name in Grafana dashboards.
That's Spring Boot. Mark it forged?
10 min read · try the examples if you haven't