Helm
The package manager for Kubernetes. Helm bundles related k8s manifests (Deployment + Service + ConfigMap + Ingress + …) into a versioned, parameterizable chart.
Why it exists
Without Helm, deploying a non-trivial app means kubectl apply -f over a dozen YAML files, with environment-specific substitutions done manually or via a templating tool. Helm gives you:
- Templating — one set of YAML with
{{ .Values.x }}placeholders, rendered per environment. - Packaging — a chart is a versioned tarball you can publish/share.
- Release tracking — Helm remembers what version of what chart with what values is currently installed; rollback to previous revision in one command.
- Dependencies — chart A can declare chart B as a dependency.
Chart layout
orders/
├── Chart.yaml # name, version, dependencies
├── values.yaml # default values
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── configmap.yaml
│ ├── _helpers.tpl # reusable template snippets
│ └── NOTES.txt # printed after install
└── charts/ # sub-chart dependencies
Chart.yaml:
apiVersion: v2
name: orders
description: Orders service
type: application
version: 1.2.3 # the chart version
appVersion: "1.0.0" # the app image version (informational)
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
values.yaml:
replicaCount: 3
image:
repository: my-org/orders
tag: "1.0.0"
resources:
requests: { cpu: 200m, memory: 256Mi }
limits: { cpu: 1, memory: 512Mi }
ingress:
enabled: true
host: api.example.com
A template using values:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-orders
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources: {{- toYaml .Values.resources | nindent 10 }}
Installing / upgrading
# Install
helm install orders ./orders -n prod -f values-prod.yaml
# Upgrade or install (idempotent)
helm upgrade --install orders ./orders -n prod -f values-prod.yaml
# See what's deployed
helm list -n prod
helm get values orders -n prod
helm get manifest orders -n prod # rendered YAML actually applied
# Roll back
helm history orders -n prod
helm rollback orders 3 -n prod # back to revision 3
helm template (no install) renders to stdout — useful for CI dry-runs and gitops.
Values precedence
From lowest to highest:
values.yamlin the chart.- Sub-chart values.
-f values-prod.yamlon the CLI.--set key=valueon the CLI.
Per-environment pattern: one chart, N values files (values-dev.yaml, values-staging.yaml, values-prod.yaml).
Common patterns
A _helpers.tpl for shared snippets
{{- define "orders.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end -}}
metadata:
labels: {{- include "orders.labels" . | nindent 4 }}
Reusable; consistent labels everywhere.
Conditionally render
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-orders
spec:
rules:
- host: {{ .Values.ingress.host }}
...
{{- end }}
Hooks for migrations
metadata:
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
Runs a Job before the Deployment is applied — good place for alembic upgrade head. Caveat: hooks aren’t transactional with the rest of the release; a failing hook leaves a half-applied state.
Helm vs Kustomize
| Helm | Kustomize | |
|---|---|---|
| Approach | Go templating | overlay/patch |
| Best for | parameterizable charts you share | environment-specific overlays of your own YAML |
| Tooling | separate helm CLI |
built into kubectl apply -k |
| Complexity | indent gymnastics in templates | YAML-native, no templating |
Many teams use both: Helm for third-party charts (postgres, kafka, cert-manager), Kustomize for in-house apps. Or skip Helm entirely for in-house and use Kustomize + GitOps.
GitOps with Helm
ArgoCD or Flux render charts and apply them, with the git repo as the source of truth.
# Argo Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: orders, namespace: argocd }
spec:
source:
repoURL: https://github.com/myorg/charts
path: orders
targetRevision: main
helm:
valueFiles: [values-prod.yaml]
destination: { server: https://kubernetes.default.svc, namespace: prod }
syncPolicy:
automated: { prune: true, selfHeal: true }
Change values in git → Argo detects → applies. No one runs helm upgrade from a laptop.
Common bugs
- Indentation hell.
nindent+toYamlfor nested objects, otherwise you get cryptic YAML errors.helm templateto see the rendered output. - Forgetting
--namespace. Helm doesn’t infer namespace fromkubectl config; default isdefault. .Release.Namecollisions. Two releases of the same chart in one namespace → resources collide.- Secrets in values files. Don’t put secrets in
values-prod.yamlin git. Use external secret store + reference, or SOPS-encrypted values, or sealed-secrets. - Hook failures leave state half-applied. Hooks don’t rollback the release on failure; the migration Job fails, the Deployment is half-rolled-out.
- Chart bloat.
if/rangeeverywhere makes templates unreadable. If a chart has >10 toggles, you’re probably misusing it — split it.
Interview angle
- “What is Helm?” — package manager for Kubernetes; charts bundle parameterizable YAML with versioning, dependencies, and release tracking. Replaces hand-managing 10+ YAML files per app.
- “Helm vs Kustomize?” — Helm is templating + packaging (share charts across orgs). Kustomize is overlay-based (environment-specific patches). Use Helm for third-party (postgres, kafka); Kustomize fits in-house apps with environment variants.
- “How do you do DB migrations with Helm?” —
pre-upgradehook runs a Job that runsalembic upgrade head(or equivalent) before the new pods come up. Watch for: hook failures leaving release in inconsistent state; race with multi-pod migrations. - “How do you parameterize for multiple environments?” — one chart + one
values.yamlper environment (values-dev.yaml,values-prod.yaml), applied withhelm upgrade --install -f values-prod.yaml. Don’t fork the chart per env. - “Where do secrets live in a Helm chart?” — never plain in
values.yamlin git. Either External Secrets Operator + reference in the chart, or SOPS-encrypted values, or sealed-secrets.