Spring Boot on Kubernetes: Health Probes, Config, Scaling & Observability
Running Spring Boot in Kubernetes well requires integrating with five areas: health probes (Spring Actuator /health/liveness and /health/readiness mapping exactly to Kubernetes probe types), configura
Running Spring Boot in Kubernetes well requires integrating with five areas: health probes (Spring Actuator /health/liveness and /health/readiness mapping exactly to Kubernetes probe types), configuration management (ConfigMaps and Secrets as environment variables or volume-mounted files), graceful shutdown (server.shutdown: graceful + terminationGracePeriodSeconds), Prometheus metrics via Micrometer, and resource sizing based on real JVM heap and non-heap memory measurement rather than guessing.
-
Spring Boot Actuator exposes /actuator/health with sub-endpoints: /actuator/health/liveness (should the container restart?) and /actuator/health/readiness (should this Pod receive traffic?). Map these directly to livenessProbe and readinessProbe.
-
Startup probe uses /actuator/health/liveness with a long initialDelaySeconds or failureThreshold -- prevents liveness killing the Pod before initial startup completes (JVM warm-up, data loading).
-
ConfigMaps mount as environment variables (envFrom: configMapRef) or as files (volumeMounts to a directory the application reads). Secrets mount identically but for sensitive values.
-
server.shutdown: graceful + spring.lifecycle.timeout-per-shutdown-phase: 20s gives Spring Boot time to finish in-flight requests. Pair with terminationGracePeriodSeconds on the Pod spec (longer than the graceful shutdown timeout).
-
Micrometer with micrometer-registry-prometheus adds auto-instrumented JVM, HTTP, and Tomcat metrics on /actuator/prometheus in Prometheus text format.
-
JVM memory sizing: Kubernetes limits set the container ceiling. JVM calculates MaxHeapSize as a fraction of available memory by default (-XX:MaxRAMPercentage=75 is a common starting point), leaving room for non-heap (metaspace, direct buffers, code cache).
-
Add spring-boot-starter-actuator and micrometer-registry-prometheus to pom.xml/build.gradle.
-
Configure management.endpoint.health.probes.enabled: true and management.health.livenessState.enabled/readinessState.enabled.
-
Map /actuator/health/liveness to livenessProbe and /actuator/health/readiness to readinessProbe in the Deployment.
-
Add startup probe with high failureThreshold for long JVM startup (data loading, connection pool warm-up).
-
Configure ConfigMaps for non-sensitive app config and Secrets for DB passwords, API keys.
-
Set server.shutdown: graceful and terminationGracePeriodSeconds (longer than the shutdown timeout).
-
Set -XX:MaxRAMPercentage=75 as JVM option; set container memory request and limit to control total JVM memory.
-
Zero-downtime rolling deploys of Spring Boot microservices via readiness probe gating.
-
Startup probe preventing liveness restarts during a data-loading Spring Boot application's 90-second startup.
-
Micrometer metrics feeding Grafana dashboards with JVM heap, GC pause times, and HTTP request latency P99.
-
ConfigMap-driven feature flags that can be updated and re-mounted without rolling a new container image.
-
Always define all three probe types: startup, liveness, and readiness -- each serves a distinct purpose.
-
Use /actuator/health/liveness for liveness (not /actuator/health which includes readiness indicators).
-
Set terminationGracePeriodSeconds to at least 10s more than spring.lifecycle.timeout-per-shutdown-phase.
-
Use -XX:MaxRAMPercentage rather than explicit -Xmx to automatically scale heap with container memory limits.
-
Using /actuator/health (aggregate) for the liveness probe -- a Down database will cause Pods to restart unnecessarily.
-
Setting terminationGracePeriodSeconds equal to or shorter than the graceful shutdown timeout -- SIGKILL arrives before drain completes.
-
Not setting JVM memory flags -- JVM may see node physical memory (much larger than container limit) and set heap too large.
-
Forgetting startup probe -- liveness probe kills the Pod during the JVM's normal startup window if initialDelaySeconds is too short.
-
Use GraalVM native image for Spring Boot applications with strict cold-start latency requirements.
-
Use Spring Boot's lazy initialization (spring.main.lazy-initialization: true) to reduce startup time at the cost of first-request latency.
-
Use -XX:+UseG1GC or -XX:+ZGC based on heap size and GC pause tolerance.
-
Run at least 2 replicas of every Spring Boot production service -- single-replica services have zero fault tolerance.
-
Set PodDisruptionBudget (minAvailable: 1) so node drain does not take the only instance offline.
-
Monitor JVM heap utilization and GC metrics (micrometer JVM metrics) in Grafana for memory pressure detection.
-
Deploy a Spring Boot application and confirm that Kubernetes invokes /actuator/health/liveness and /actuator/health/readiness as probes.
-
Simulate a graceful shutdown by running kubectl delete pod and observing that in-flight requests complete before the process exits.
-
View JVM metrics in Prometheus/Grafana after deploying micrometer-registry-prometheus.
-
Map Spring Actuator /health/liveness to livenessProbe and /health/readiness to readinessProbe exactly.
-
Startup probe prevents liveness from killing the Pod during normal JVM startup.
-
server.shutdown: graceful + terminationGracePeriodSeconds > shutdown timeout = zero-downtime deploys.
-
Use -XX:MaxRAMPercentage=75 to scale JVM heap automatically with container memory limits.
-
Micrometer + Prometheus provides full JVM, HTTP, and GC observability with no manual instrumentation.
Want a visual for this concept?
Generate a diagram tailored to “Spring Boot on Kubernetes: Health Probes, Config, Scaling & Observability” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →