Helm β Package Manager for Kubernetes
Helm is the Kubernetes equivalent of
apt,brew, ormavenβ it packages, versions, and deploys Kubernetes applications.
Why Helm?β
Without Helm, deploying an application means maintaining dozens of individual YAML files.
With Helm, it's a versioned, parameterised chart with a single command to install.
# Without Helm: apply 8+ files, manage env differences manually
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/pvc.yaml
kubectl apply -f k8s/hpa.yaml
# With Helm: one command
helm install my-api ./my-api-chart -f values.production.yaml
Core Conceptsβ
| Term | Definition |
|---|---|
| Chart | Package of K8s YAML templates + default values |
| Release | Installed instance of a chart |
| Values | Configuration that parameterises the chart |
| Repository | Remote store of charts (like Maven Central) |
| Template | YAML file with Go template variables |
| Revision | Numbered history of a release's changes |
Helm Chart Structureβ
my-api/ β Chart directory (name = chart name)
βββ Chart.yaml β Chart metadata
βββ values.yaml β Default configuration values
βββ values.production.yaml β Production overrides (not standard, by convention)
βββ charts/ β Sub-charts (dependencies)
βββ templates/ β K8s YAML templates
β βββ _helpers.tpl β Reusable template helpers (partial templates)
β βββ deployment.yaml
β βββ service.yaml
β βββ ingress.yaml
β βββ configmap.yaml
β βββ hpa.yaml
β βββ serviceaccount.yaml
β βββ NOTES.txt β Displayed after install
βββ .helmignore β Files to exclude from packaging
Chart.yamlβ
apiVersion: v2 # v2 for Helm 3
name: my-api
description: Spring Boot REST API service
type: application # application | library
version: 1.2.0 # Chart version (semver)
appVersion: "2.5.0" # App version (informational)
keywords:
- api
- spring-boot
home: https://github.com/myorg/my-api
maintainers:
- name: Backend Team
# Chart dependencies (sub-charts)
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # Only include if postgresql.enabled=true
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
values.yaml (Default Values)β
# values.yaml β these are the defaults
replicaCount: 2
image:
repository: myapp
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: false
className: nginx
host: api.example.com
tls: false
tlsSecretName: ""
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
config:
springProfile: default
logLevel: INFO
dbHost: localhost
dbPort: 5432
dbName: mydb
postgresql:
enabled: true # Install sub-chart
auth:
database: mydb
username: myapp
password: ""
redis:
enabled: false
nodeSelector: {}
tolerations: []
affinity: {}
Templatesβ
Go template syntax renders values into YAML.
deployment.yamlβ
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-api.fullname" . }} # From _helpers.tpl
namespace: {{ .Release.Namespace }}
labels:
{{- include "my-api.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-api.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-api.selectorLabels" . | nindent 8 }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
# β Forces Pod restart when ConfigMap changes
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.targetPort }}
env:
- name: SPRING_PROFILES_ACTIVE
value: {{ .Values.config.springProfile | quote }}
- name: DB_HOST
value: {{ .Values.config.dbHost | quote }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "my-api.fullname" . }}-secret
key: db-password
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
_helpers.tpl (Reusable Partials)β
{{/*
Expand the name of the chart.
*/}}
{{- define "my-api.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "my-api.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "my-api.labels" -}}
helm.sh/chart: {{ include "my-api.chart" . }}
{{ include "my-api.selectorLabels" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "my-api.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-api.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
Helm CLI Commandsβ
# βββ Repository Management ββββββββββββββββββββββββββββββββββββββββ
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo update # Fetch latest chart versions
helm repo list
helm repo remove bitnami
# βββ Search βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
helm search repo postgres # Search added repos
helm search hub nginx # Search Artifact Hub (all public charts)
helm search repo bitnami/postgresql --versions # List all versions
# βββ Install ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
helm install my-api ./my-api # From local chart
helm install my-api bitnami/postgresql # From repo
helm install my-api ./my-api \
-f values.production.yaml # Custom values file
helm install my-api ./my-api \
--set image.tag=1.3.0 \ # Inline value override
--set replicaCount=3
helm install my-api ./my-api \
--namespace production \
--create-namespace \ # Create namespace if needed
--wait # Wait for pods to be ready
# Dry run (render templates without installing)
helm install my-api ./my-api --dry-run --debug
# βββ Upgrade ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
helm upgrade my-api ./my-api
helm upgrade my-api ./my-api -f values.production.yaml
helm upgrade my-api ./my-api --set image.tag=1.4.0
helm upgrade my-api ./my-api --reuse-values # Keep existing values, only change specified
# Install if not exists, upgrade if exists
helm upgrade --install my-api ./my-api \
-f values.production.yaml \
--namespace production \
--create-namespace
# βββ Rollback βββββββββββββββββββββββββββββββββββββββββββββββββββββ
helm rollback my-api # Roll back to previous release
helm rollback my-api 2 # Roll back to revision 2
# βββ Status / History βββββββββββββββββββββββββββββββββββββββββββββ
helm list # All releases in current namespace
helm list -n production
helm list -A # All namespaces
helm status my-api # Release status
helm history my-api # Release revision history
# βββ Uninstall ββββββββββββββββββββββββββββββββββββββββββββββββββββ
helm uninstall my-api
helm uninstall my-api -n production
helm uninstall my-api --keep-history # Uninstall but keep history
# βββ Template Debugging βββββββββββββββββββββββββββββββββββββββββββ
helm template my-api ./my-api # Render templates to stdout
helm template my-api ./my-api -f values.production.yaml
helm template my-api ./my-api --debug # Verbose render with parsed values
helm lint ./my-api # Lint chart for errors
# βββ Inspect Chart ββββββββββββββββββββββββββββββββββββββββββββββββ
helm show chart bitnami/postgresql # Chart.yaml
helm show values bitnami/postgresql # Default values.yaml
helm show all bitnami/postgresql # Everything
# βββ Package and Push βββββββββββββββββββββββββββββββββββββββββββββ
helm package ./my-api # Creates my-api-1.2.0.tgz
helm push my-api-1.2.0.tgz oci://registry.example.com/charts
Values Override Patternsβ
# Multiple values files (merged in order, later files win)
helm upgrade --install my-api ./my-api \
-f values.yaml \ # Base defaults
-f values.production.yaml \ # Production overrides
-f values.secrets.yaml # Secret values (not committed to git)
# Inline override (highest precedence)
helm upgrade my-api ./my-api \
--set image.tag=$CI_COMMIT_SHA \
--set replicaCount=5
Dependencies (Sub-Charts)β
# After adding dependencies to Chart.yaml:
helm dependency update ./my-api # Downloads sub-charts to charts/
# Lock dependencies to exact versions
helm dependency build ./my-api # Generates Chart.lock
# Install with embedded dependencies
helm install my-api ./my-api # Deploys app + postgres + redis
Creating a Chart from Scratchβ
# Scaffold a new chart
helm create my-api
# Creates standard directory structure with example templates
# Edit Chart.yaml, values.yaml, templates/
# Delete templates you don't need
# Test rendering
helm template my-api ./my-api
# Lint
helm lint ./my-api
# Install
helm install my-api ./my-api --dry-run
helm install my-api ./my-api
Interview Questionsβ
- What is Helm and what problem does it solve?
- What is the difference between a Helm chart and a Helm release?
- What is
values.yamlused for? - How do you override chart values at install time?
- How do you roll back a Helm release?
- What does
helm upgrade --installdo? - What is
helm templateused for? - What is a chart dependency?
- How does Helm manage release history?
- What is
helm lintand why should you run it in CI?