Helm charts are not just for packaging; they’re the orchestrator for your Spring Boot application’s life on Kubernetes, and probes are the sentinels that ensure it stays alive and ready.
Let’s watch this Spring Boot app, my-awesome-app, deploy. We’ll use a Helm chart to manage it.
First, the Chart.yaml:
apiVersion: v2
name: my-awesome-app
version: 0.1.0
appVersion: 1.0.0
description: A Helm chart for my awesome Spring Boot app
Next, values.yaml to configure our deployment:
replicaCount: 2
image:
repository: my-docker-repo/my-awesome-app
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
livenessProbe:
enabled: true
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3
httpGet:
path: /actuator/health/liveness
port: 8080
readinessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
httpGet:
path: /actuator/health/readiness
port: 8080
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
And finally, the core of our deployment in templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-awesome-app.fullname" . }}
labels:
{{- include "my-awesome-app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-awesome-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-awesome-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080 # Spring Boot's default HTTP port
protocol: TCP
livenessProbe:
{{- if .Values.livenessProbe.enabled }}
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
httpGet:
path: {{ .Values.livenessProbe.httpGet.path }}
port: {{ .Values.livenessProbe.httpGet.port }}
{{- end }}
readinessProbe:
{{- if .Values.readinessProbe.enabled }}
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
httpGet:
path: {{ .Values.readinessProbe.httpGet.path }}
port: {{ .Values.readinessProbe.httpGet.port }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
And templates/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-awesome-app.fullname" . }}
labels:
{{- include "my-awesome-app.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "my-awesome-app.selectorLabels" . | nindent 4 }}
To deploy this, you’d first package your Spring Boot app as a Docker image and push it to a registry. Then, assuming you have Helm and kubectl configured for your Kubernetes cluster:
helm install my-awesome-app ./my-awesome-app-chart
Kubernetes creates two Pods for my-awesome-app. Each Pod contains a container running your Spring Boot application. The deployment.yaml specifies livenessProbe and readinessProbe configurations.
The livenessProbe is your application’s "Are you alive?" check. If it fails repeatedly (after initialDelaySeconds and periodSeconds), Kubernetes considers the container unhealthy and restarts it. For Spring Boot, the /actuator/health/liveness endpoint, provided by the Spring Boot Actuator, is the standard. It typically returns a 200 OK if the application is running, and 500 Internal Server Error or other non-200 status codes if it’s in a bad state (e.g., database connection lost, critical component failed).
The readinessProbe is the "Are you ready to receive traffic?" check. If this probe fails, Kubernetes removes the Pod’s IP address from the Service’s endpoints, meaning no new traffic will be sent to it. This is crucial during application startup. Your Spring Boot app might be technically "alive" but still initializing its database connections or warming up caches. Once the readinessProbe starts returning 200 OK consistently, Kubernetes adds the Pod back to the Service’s endpoints. The /actuator/health/readiness endpoint is commonly used here; it’s often configured to be more stringent than liveness, checking for full operational readiness.
The initialDelaySeconds in both probes gives your application time to start up before Kubernetes begins probing. periodSeconds defines how often the probe is executed. failureThreshold is the number of consecutive failures before the probe is considered failed.
The beauty of using Helm is that these probe configurations, along with image details, replica counts, and resource requests/limits, are all managed declaratively in values.yaml. You can easily scale your application, adjust probe timings, or change resource allocations by modifying the values.yaml file and running helm upgrade my-awesome-app ./my-awesome-app-chart.
The critical piece here is that Spring Boot Actuator must be included as a dependency in your pom.xml or build.gradle to expose these /actuator/health endpoints. Otherwise, the httpGet probes will consistently fail with 404 Not Found or connection refused errors, leading to unpredictable pod behavior.
After deploying and assuming everything is configured correctly, the next thing you’ll likely encounter is managing secrets for sensitive configuration like database passwords.