Contents
- Chart Structure
- Chart.yaml
- values.yaml — Defaults
- Deployment Template
- _helpers.tpl — Named Templates
- Environment-Specific Overrides
- Install, Upgrade & Rollback
- Helm Hooks
- Helmfile — Multi-Service Deployments
The output or file structure below illustrates the expected result.
payment-service/ ← chart name
├── Chart.yaml ← chart metadata (name, version, appVersion)
├── values.yaml ← default values
├── values-staging.yaml ← staging overrides
├── values-production.yaml ← production overrides
└── templates/
├── _helpers.tpl ← reusable named templates
├── deployment.yaml
├── service.yaml
├── ingress.yaml
├── configmap.yaml
├── hpa.yaml
├── serviceaccount.yaml
└── NOTES.txt ← printed after install
# Create scaffold with helm create
helm create payment-service
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
# Chart.yaml
apiVersion: v2
name: payment-service
description: Spring Boot payment microservice
type: application
version: 0.1.0 # chart version — increment on chart changes
appVersion: "1.0.0" # application version — the Docker image tag
maintainers:
- name: platform-team
email: platform@example.com
dependencies:
# Optional — pull in a PostgreSQL sub-chart for local dev
- name: postgresql
version: "14.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled # only install if enabled in values
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
# values.yaml
replicaCount: 2
image:
repository: myorg/payment-service
tag: "" # defaults to Chart.appVersion if empty
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: false
className: nginx
host: payment.example.com
tls: true
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
springProfiles: "production"
config:
logLevel: INFO
maxRetryAttempts: 3
postgresql:
enabled: false # disable by default — use external DB in production
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
{{/* templates/deployment.yaml */}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "payment-service.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "payment-service.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "payment-service.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "payment-service.selectorLabels" . | nindent 8 }}
annotations:
# Force pod restart when ConfigMap changes (checksum annotation)
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: {{ .Values.springProfiles | quote }}
- name: JAVA_TOOL_OPTIONS
value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
envFrom:
- configMapRef:
name: {{ include "payment-service.fullname" . }}-config
resources:
{{- toYaml .Values.resources | nindent 12 }}
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 15
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
{{/* templates/_helpers.tpl */}}
{{/* Full name: release-name + chart-name, capped at 63 chars */}}
{{- define "payment-service.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/* Common labels */}}
{{- define "payment-service.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
{{ include "payment-service.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/* Selector labels */}}
{{- define "payment-service.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
# values-production.yaml — overrides on top of values.yaml defaults
replicaCount: 5
image:
tag: "1.2.3" # pin exact version in production
ingress:
enabled: true
host: api.example.com
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 2Gi
config:
logLevel: WARN
The commands below build and run the application. Make sure Docker is running locally before executing the image build steps.
# Lint — validate templates before deploying
helm lint payment-service/ -f values-production.yaml
# Dry-run — render templates without applying
helm install payment-service ./payment-service \
--namespace payments \
--values values-production.yaml \
--dry-run --debug
# Install
helm install payment-service ./payment-service \
--namespace payments --create-namespace \
--values values-production.yaml
# Upgrade — deploy new image version
helm upgrade payment-service ./payment-service \
--namespace payments \
--values values-production.yaml \
--set image.tag=1.3.0 \
--atomic # auto-rollback if upgrade fails
--wait # wait for pods to be ready
# View release history
helm history payment-service -n payments
# Rollback to previous revision
helm rollback payment-service -n payments
# Rollback to specific revision
helm rollback payment-service 2 -n payments
# Uninstall
helm uninstall payment-service -n payments
Hooks let you run jobs at specific points in the release lifecycle — e.g., run a database migration before the deployment is upgraded.
# templates/db-migrate-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "payment-service.fullname" . }}-db-migrate
annotations:
"helm.sh/hook": pre-upgrade,pre-install # run before the upgrade/install
"helm.sh/hook-weight": "-5" # lower runs first
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
command: ["java", "-jar", "app.jar", "--spring.batch.job.enabled=true"]
env:
- name: SPRING_PROFILES_ACTIVE
value: "migrate"
Helmfile declaratively manages multiple Helm releases — useful for deploying a full microservices stack with one command.
# helmfile.yaml
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
releases:
- name: payment-service
namespace: payments
chart: ./charts/payment-service
values:
- ./values/payment-service/values-{{ .Environment.Name }}.yaml
- name: order-service
namespace: orders
chart: ./charts/order-service
values:
- ./values/order-service/values-{{ .Environment.Name }}.yaml
- name: kafka
namespace: kafka
chart: bitnami/kafka
version: "26.x.x"
values:
- ./values/kafka/values-{{ .Environment.Name }}.yaml
environments:
staging:
values:
- env: staging
production:
values:
- env: production
# Deploy all services to staging
helmfile -e staging apply
# Diff before applying to production
helmfile -e production diff
# Apply to production
helmfile -e production apply