Scale and Monitor Spring Boot in Production

Khimananda Oli 9 min read Programming and Languages
Scale and Monitor Spring Boot in Production

By Khimananda Oli | Last reviewed: August 2026

Running a Java service on your laptop is fundamentally different from operating it under load in a distributed system. To successfully scale and monitor Spring Boot in production, you must move beyond default configurations and treat observability as a first-class architectural requirement rather than an afterthought. This means exposing standardized metrics via the Actuator, configuring stateless instances for horizontal scaling, and defining clear Service Level Objectives (SLOs) before traffic spikes hit. For teams managing critical infrastructure, aligning these practices with the four golden signals of monitoring provides the necessary foundation for reliability.

Ingress / LBTraffic EntrySpring Boot Pod AActuator + MicrometerSpring Boot Pod BStateless InstancePrometheusMetrics ScrapingGrafanaDashboards & SLOs
High-level architecture to scale and monitor Spring Boot in production: traffic flows through an ingress controller to stateless pods that expose metrics for Prometheus scraping and Grafana visualization.

How do you configure Spring Boot Actuator for safe production monitoring?

The Spring Boot Actuator is the primary interface for observing application health, but its default configuration is designed for local development, not public-facing environments. In production, you must explicitly define which endpoints are exposed and secure them against unauthorized access. Exposing everything via management.endpoints.web.exposure.include=* is a security risk that can leak environment variables, heap dumps, or configuration details to attackers.

Selective Endpoint Exposure

For most production workloads, you only need three core endpoints: health, prometheus, and info. The health endpoint supports liveness and readiness probes for Kubernetes, while the prometheus endpoint provides the metric format required by modern observability stacks. Configure this in your application.yml:

management:
  endpoints:
    web:
      exposure:
        include: health,prometheus,info
      base-path: /internal/actuator
  endpoint:
    health:
      show-details: never
      probes:
        enabled: true
  metrics:
    tags:
      application: ${spring.application.name}
      environment: ${APP_ENV:production}

Moving the actuator to a separate base path like /internal/actuator allows you to apply network policies or ingress rules that block external access entirely. Never expose actuator endpoints on the same public port as your business API without strict authentication. For deeper guidance on securing these interfaces, refer to Ubuntu security hardening principles which apply equally to containerized Java workloads at the host level.

Custom Health Indicators

Built-in health checks verify database and cache connectivity, but they often miss critical dependencies like third-party payment gateways or message brokers. Implement custom HealthIndicator beans for every external system your service depends on. This ensures that a downstream outage triggers a proper health degradation signal, preventing the orchestrator from routing traffic to instances that cannot actually serve requests.

What metrics matter most when you scale and monitor Spring Boot in production?

Collecting thousands of JVM metrics creates noise, not insight. When you scale and monitor Spring Boot in production, focus on metrics that directly correlate with user experience and system capacity. The Micrometer library abstracts metric collection, allowing you to export to Prometheus, Datadog, or CloudWatch without changing instrumentation code.

Application Code@Timed / CountersMicrometer RegistryNormalization/actuator/prometheusText Format ExportMonitoring BackendStorage & Alerting
Micrometer metrics flow: application instrumentation passes through the registry for normalization before being exported via the actuator endpoint to your monitoring backend.

The Essential Metric Set

  • HTTP Request Latency (Histogram): Use @Timed or the WebMvc auto-configuration to capture p50, p95, and p99 latencies. Average latency hides tail issues; histograms reveal them.
  • Error Rate: Track HTTP 5xx responses separately from 4xx. A spike in 5xx indicates system failure, while 4xx usually indicates client behavior. Calculate error rate as rate(http_server_requests_seconds_count{status=~"5.."}[5m]).
  • JVM Memory Pressure: Monitor jvm_memory_used_bytes specifically for the Old Gen and Metaspace. Frequent GC pauses or Old Gen filling up are precursors to out-of-memory crashes that restart loops won't fix.
  • Thread Pool Saturation: For Tomcat or Reactor Netty, track active threads versus max threads. When active threads consistently hit 80% of max, latency increases non-linearly even before CPU saturates.
  • Business Metrics: Orders processed, payments completed, or messages consumed per second. These validate that technical health translates to business value.

Cardinality Control

A common mistake when instrumenting Spring Boot is adding high-cardinality tags like userId, traceId, or raw URLs to metrics. This explodes the time-series database storage and slows down queries. Always use URI templates (/api/users/{id}) instead of actual paths, and limit tag combinations to fewer than 10,000 unique series per metric. If you need per-user analytics, send that data to logs or a dedicated analytics pipeline, not your metrics store. For comprehensive logging strategies that complement metrics, see structured logging best practices.

How does Kubernetes HPA work with Spring Boot for automatic scaling?

Kubernetes Horizontal Pod Autoscaler (HPA) adjusts replica counts based on observed metrics. While CPU utilization is the default trigger, it is often inadequate for Java applications because the JVM manages memory independently of CPU load. A Spring Boot service can exhaust heap space and crash while CPU usage remains below 50%. Effective autoscaling requires custom metrics that reflect actual application pressure.

Configuring Custom Metric Scaling

To scale based on HTTP request rate or thread pool saturation, you need the Prometheus Adapter installed in your cluster. This component translates PromQL queries into the Kubernetes Metrics API format that HPA consumes. Define an HPA manifest targeting your deployment:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: spring-boot-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: spring-boot-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_server_requests_seconds_count
      target:
        type: AverageValue
        averageValue: "100"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300

This configuration scales up when average requests per pod exceed 100 RPS, but includes a stabilization window to prevent flapping. The scaleDown window is intentionally longer (5 minutes) because Java startup times are significant; aggressively scaling down only to immediately scale back up wastes resources and risks availability during warm-up periods.

Readiness Gates and Graceful Shutdown

Scaling is useless if new pods receive traffic before they are ready. Spring Boot 3.x supports graceful shutdown natively. Enable it alongside Kubernetes probes to ensure zero-downtime scaling events:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

Without graceful shutdown, in-flight requests are terminated when a pod scales down or restarts, causing visible errors for users. Combined with proper readiness probes that check application context initialization, this ensures that scaling events are transparent to clients. Teams deploying on managed platforms should review horizontal pod autoscaling in Kubernetes for platform-specific tuning parameters.

Which observability stack should you choose for Spring Boot in 2026?

The tooling landscape for Java observability has matured significantly. Your choice depends on operational capacity, budget, and compliance requirements. There is no single best option, only trade-offs appropriate for different organizational contexts.

CriteriaPrometheus + GrafanaElastic Stack (ELK)Managed (Datadog/New Relic)
Setup ComplexityModerate (self-hosted)High (multiple components)Low (agent-only)
Cost at ScaleLow (compute-bound)Medium-High (storage-heavy)High (per-host/metric pricing)
Spring Boot IntegrationNative (Micrometer)Requires Logstash/FilebeatNative (Java Agent)
Data RetentionShort-term (weeks)Long-term (months/years)Configurable (tiered pricing)
Compliance Audit TrailManual evidence collectionBuilt-in audit loggingSOC 2 reports provided
Best ForMetrics-first, cost-sensitiveLog-heavy, compliance-focusedSmall teams, fast time-to-value

For most teams starting fresh in 2026, Prometheus and Grafana remain the pragmatic default for metrics due to native Spring Boot support and predictable costs. Pair this with Loki or OpenSearch for logs to avoid ELK's operational overhead. If your organization requires SOC 2 compliance and lacks dedicated platform engineering staff, managed solutions justify their premium through reduced toil and pre-built compliance artifacts. Remember that observability tools themselves require monitoring; a silent failure in your metrics pipeline is worse than having no monitoring at all.

CPU Utilization❌ Misses Memory LeaksJIT Compilation Skews DataHeap Memory Usage⚠️ Reactive But LaggingGC Pauses Before Scale-UpRequest Rate / Latency✅ Proactive & Business-AlignedScales Before User ImpactRecommendation: Combine Custom Metrics + Memory CeilingUse request rate for proactive scaling; add memory threshold as safety guardrail
Scaling trigger comparison for Spring Boot: custom metrics provide proactive scaling aligned with business load, while CPU alone fails to capture JVM-specific resource constraints.

How do you implement SLO-based alerting for Spring Boot services?

Alerting on raw thresholds ("CPU > 80%") generates fatigue and misses real user pain. Service Level Objectives (SLOs) define acceptable reliability targets and alert only when error budgets are burning too fast. For a Spring Boot API, a typical SLO might be "99.9% of requests return successfully within 500ms over a 30-day window."

Defining the Error Budget

Translate your SLO into a measurable error budget. At 99.9% availability, you have a 0.1% error budget, meaning roughly 43 minutes of downtime or degraded responses per month. Configure Prometheus recording rules to calculate the burn rate:

groups:
- name: spring_boot_slo
  rules:
  - record: slo:http_request_success:ratio_rate5m
    expr: |
      sum(rate(http_server_requests_seconds_count{status!~"5.."}[5m]))
      /
      sum(rate(http_server_requests_seconds_count[5m]))
  - alert: HighErrorBudgetBurn
    expr: slo:http_request_success:ratio_rate5m < 0.999
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "Spring Boot API consuming error budget too fast"
      description: "Current success ratio {{ $value }} is below 99.9% SLO"

This approach alerts when the rate of budget consumption threatens your monthly target, not when an arbitrary number crosses a line. It reduces false positives during expected maintenance windows and focuses on-call attention on genuine reliability degradation. Define meaningful SLIs and SLOs collaboratively with product teams to ensure they reflect actual user tolerance, not engineering perfectionism.

Reliable Operations Start With Intentional Design

When you scale and monitor Spring Boot in production effectively, you shift from reactive firefighting to proactive capacity management. The combination of selective actuator exposure, Micrometer-driven custom metrics, Kubernetes HPA tuned for JVM behavior, and SLO-based alerting creates a feedback loop where scaling decisions are informed by actual user experience rather than guesswork. Start by auditing your current actuator configuration and identifying one business-critical metric to drive autoscaling this quarter. If your team needs help designing an observability strategy that survives audit season and traffic spikes alike, reach out to discuss your specific architecture.

Frequently Asked Questions

Use Kubernetes HPA with custom metrics from Micrometer. Configure resource requests accurately and implement readiness probes to prevent traffic routing to unready pods during scaling events.

Set -XX:MaxRAMPercentage=75.0 instead of fixed heap sizes. This allows the JVM to respect container memory limits dynamically, preventing OOM kills while utilizing available resources efficiently in production environments.

Prometheus and Grafana remain the standard. Use Micrometer Registry Prometheus for metrics export and OpenTelemetry for distributed tracing across microservices without vendor lock-in or excessive overhead.

Distinguish between liveness and readiness probes. Readiness should check database and cache connectivity, while liveness only verifies process health. Misconfigured dependencies in liveness probes cause unnecessary restarts.

Java 21+ virtual threads reduce thread pool exhaustion under high concurrency. Enable via spring.threads.virtual.enabled=true to handle thousands of concurrent requests without increasing memory footprint or CPU context switching costs.

Class loading and bean initialization dominate startup time. Use GraalVM native images or Spring AOT processing to reduce startup from seconds to milliseconds, critical for autoscaling responsiveness in cloud environments.

Expose HikariCP metrics through Micrometer. Track active connections, pending threads, and connection wait times. Alert when active connections exceed 80% of maximum pool size to prevent request queuing.

Yes, externalize sessions for horizontal scaling. Redis offers persistence and pub/sub for multi-datacenter setups, while Memcached provides lower latency for simple key-value session storage without durability requirements.

Implement structured JSON logging with sampling. Filter DEBUG logs in production, use log levels dynamically via Spring Boot Admin, and ship only ERROR and WARN levels to expensive observability backends.

Enable Content-Security-Policy, Strict-Transport-Security, and X-Content-Type-Options via Spring Security. Disable server headers exposing version info and enforce HTTPS-only cookies for session management in load-balanced environments.

Capture heap dumps via actuator endpoints during high memory usage. Analyze with Eclipse MAT to identify retained objects, focusing on cached collections, unclosed streams, and listener references preventing garbage collection.

Set server.shutdown.grace-period to 30 seconds matching your load balancer drain timeout. This ensures in-flight requests complete before pod termination, preventing 502 errors during deployments and scaling events.

Run load tests with k6 or Gatling against staging clusters. Verify HPA triggers at expected thresholds, confirm no resource throttling occurs, and measure scale-up latency against SLA requirements.

Minimal overhead exists but secure all endpoints. Expose only health, prometheus, and info externally. Rate-limit sensitive endpoints and never enable env or heapdump without authentication in production environments.

Use Spring Cloud Config with refresh scope or feature flags via LaunchDarkly. Broadcast configuration updates through message brokers to avoid rolling restarts, maintaining availability during parameter tuning in production.