The package manager for Kubernetes, hands-on. You'll install charts, template your own, manage values per environment, handle releases and rollbacks, and follow the conventions that keep charts maintainable.
| You need | Why |
|---|---|
| A Kubernetes cluster (Minikube/kind/Docker Desktop) | Helm installs releases into it |
kubectl configured for that cluster |
Helm uses your kubeconfig to talk to the API |
The helm CLI installed |
Runs every command here |
New to Kubernetes? Do the Kubernetes getting-started tutorial first. Confirm both tools are ready: helm version and kubectl get nodes.
Raw Kubernetes YAML doesn't parameterize: deploying the same app to staging and prod means copy-pasting and hand-editing manifests. Helm packages a set of templated manifests into a chart, fills them from values, and tracks each install as a versioned release you can upgrade and roll back.
chart (templates + default values) + your values -> release (installed, versioned)
Install the CLI and confirm:
# macOS: brew install helm
helm version
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install cache bitnami/redis --set architecture=standalone
cache is the release name. Inspect and manage it:
helm list
helm status cache
helm uninstall cache
Before installing, preview what a chart will create and which knobs it exposes:
helm show values bitnami/redis | less
helm template cache bitnami/redis --set architecture=standalone # render locally, apply nothing
helm template is your best friend — it renders the final YAML so you can read exactly what will hit the cluster.
helm create myapp
You get a working starter chart:
myapp/
├── Chart.yaml # name, version, appVersion
├── values.yaml # default configuration
├── templates/ # templated manifests
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── _helpers.tpl # reusable template snippets
│ └── ...
└── charts/ # bundled sub-charts (dependencies)
Render it to see the machinery:
helm template myapp ./myapp
Templates are Go templates over your values. A deployment snippet:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
ports:
- containerPort: {{ .Values.service.port }}
Key pieces:
.Values — everything from values.yaml (and --set/-f overrides)..Chart / .Release — metadata like .Release.Name, .Chart.AppVersion.include + _helpers.tpl — reuse naming/label logic instead of repeating it.nindent — indent a block correctly (indentation bugs are the #1 Helm frustration).values.yaml holds defaults; override them per environment with layered files:
# values-prod.yaml
replicaCount: 3
image:
tag: "1.4.2"
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { cpu: "1", memory: 512Mi }
helm upgrade --install myapp ./myapp -f values-prod.yaml
Precedence, lowest to highest: chart values.yaml → -f files (in order) → --set flags. Keep environment differences in values files, not in forked templates.
helm upgrade --install is the idempotent workhorse — it installs if absent, upgrades if present:
helm upgrade --install myapp ./myapp -f values-prod.yaml --atomic --wait
--wait blocks until pods are Ready.--atomic auto-rolls-back if the upgrade fails — no half-applied releases.Every upgrade is a numbered revision:
helm history myapp
helm rollback myapp 3 # revert to revision 3
Roll back is instant because Helm stores each rendered release in the cluster.
Charts can depend on other charts. Declare them in Chart.yaml:
dependencies:
- name: postgresql
version: "15.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
helm dependency update ./myapp
condition: lets consumers turn a dependency on/off from values (postgresql.enabled: false to bring your own database).
Before shipping a chart, lint and test-render it — wire these into CI:
helm lint ./myapp
helm template myapp ./myapp -f values-prod.yaml | kubectl apply --dry-run=server -f -
helm lint catches structural issues; the server-side dry-run validates the rendered manifests against the real API. Add helm test hooks for post-install smoke checks.
Chart.yaml version (SemVer) — bump it on every change.version (chart) from appVersion (the app) — they move independently.values.yaml. Reference existing Secrets, or integrate External/Sealed Secrets.values.yaml with comments — it's your chart's API.helm upgrade --install --atomic in automation; avoid helm install in CI (it fails on re-run).helm install in CI. It fails on the second run. Use helm upgrade --install — it's idempotent.indent vs nindent. The #1 Helm frustration. Use nindent to add a newline plus indentation for embedded blocks.values.yaml. It's plaintext and often committed. Reference existing Secrets, or use External/Sealed Secrets.version with appVersion. Chart version (SemVer of the packaging) and appVersion (the app inside) move independently — bump the chart version on every change.-f values-prod.yaml), not duplicated templates.helm lint plus a server-side --dry-run=server catches structural and API errors before they hit the cluster.--atomic. A failed upgrade can leave a half-applied release. --atomic --wait auto-rolls-back on failure.bitnami/redis, then run helm template and helm show values to read exactly what it creates and which knobs it exposes.helm create myapp, render it with helm template, and trace how .Values.replicaCount flows into the Deployment.values-prod.yaml (3 replicas, pinned tag, resources) and install with helm upgrade --install myapp ./myapp -f values-prod.yaml --atomic --wait.helm history, then helm rollback to the prior revision and confirm it's instant.Self-check:
helm upgrade --install instead of helm install in automation?version and appVersion?--wait and --atomic each protect you from?values.yaml.You can now consume charts, build and template your own, manage per-environment values, and upgrade/rollback safely — Helm as teams actually use it.
Learned the concepts? Test yourself with Kubernetes interview questions.
Go to the question bank →