Helm releases have a life — they are installed, upgraded as you ship new versions or change configuration, occasionally rolled back, and eventually uninstalled. This lesson walks through each transition and the safety mechanisms you should always use.
Install
helm install my-app ./my-app \
--namespace web --create-namespace \
-f values-prod.yaml \
--set image.tag=v2.7.0 \
--wait --timeout 5m
Key flags:
--namespace/-n— target namespace.--create-namespacecreates it if missing.-f file.yaml— values overrides (repeat for layered files; later wins).--set key=value— inline overrides (later wins). Nested paths use dots; arrays use brackets:--set ingress.hosts[0].host=api.acme.io.--wait— block until all resources are Ready (Deployments, StatefulSets, DaemonSets, Jobs).--timeout— how long to wait. Default 5m.--atomic— uninstall on failure (or roll back on upgrade). Use in CI.--dry-run— render and validate; do not apply.
Where Releases Live
Helm 3 stores each revision of each release as a Kubernetes Secret in the release namespace:
kubectl get secrets -l owner=helm
# NAME TYPE DATA AGE
# sh.helm.release.v1.my-app.v1 helm.sh/release.v1 1 2d
# sh.helm.release.v1.my-app.v2 helm.sh/release.v1 1 4h
# sh.helm.release.v1.my-app.v3 helm.sh/release.v1 1 1m
Each secret holds the gzipped JSON of the rendered chart and values at that revision. This is how helm history and helm rollback work.
Upgrade
helm upgrade my-app ./my-app -n web \
-f values-prod.yaml \
--set image.tag=v2.8.0 \
--atomic --wait --timeout 10m
--install makes it install-or-upgrade in one command — useful in CI:
helm upgrade --install my-app ./my-app -n web -f values-prod.yaml
Reuse vs reset values:
- Default — values from
-fand--setare merged on top of the chart's defaults. Previously set overrides from earlier installs are discarded unless re-supplied. --reuse-values— start from the last release's overrides, layer new ones on top. Quick patches.--reset-values— discard previous overrides, use only what's on this command line.--reset-then-reuse-values(Helm 3.14+) — reset, then layer the previous values back; cleanest middle ground.
The default behaviour surprises everyone at some point. Best practice: always pass -f with the same file you used last time, stored in Git, and let GitOps (or a thin wrapper script) handle this consistently.
Diff Before You Upgrade
The helm-diff plugin shows what will change:
helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade my-app ./my-app -n web -f values-prod.yaml
Output is a colour-coded patch. Make this part of your PR review process. The diff catches: image tag changes, replica count drift, label changes that would break selectors, missing required values.
History and Rollback
helm history my-app -n web
# REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
# 1 Mon May 5 10:01:00 2026 superseded my-app-1.0 2.5.0 Install complete
# 2 Tue May 13 14:22:00 2026 superseded my-app-1.1 2.6.0 Upgrade complete
# 3 Thu May 27 09:15:00 2026 deployed my-app-1.2 2.7.0 Upgrade complete
helm rollback my-app 2 -n web --wait
# Rolls back to revision 2 (creates revision 4 with that content)
Rollback creates a new revision with the old content — your history grows; nothing is destroyed. The history is bounded by --history-max (default 10).
Inspect Any Past Revision
helm get values my-app -n web --revision 2 # values used
helm get manifest my-app -n web --revision 2 # rendered YAML
helm get notes my-app -n web --revision 2 # NOTES.txt
helm get all my-app -n web --revision 2 # everything
Invaluable in incident review.
Atomic Upgrades
--atomic implies --wait and, on failure, automatically rolls the release back. In CI pipelines this is what you almost always want — you do not want a half-upgraded release sitting in the cluster after a failed deploy.
helm upgrade --install --atomic --timeout 10m my-app ./my-app -f values-prod.yaml
Stuck Releases: pending-upgrade and pending-install
If a helm upgrade is killed mid-flight (CI runner dies, kubectl times out), the release secret can be left in pending-upgrade. Subsequent commands fail with "another operation in progress".
Recovery:
helm history my-app -n web— find the lastdeployedrevisionhelm rollback my-app <last-deployed> -n web- If rollback also fails, delete the pending revision secret and retry
Uninstall
helm uninstall my-app -n web
helm uninstall my-app -n web --keep-history # preserve revision secrets
Helm uses the manifest from the last revision to compute what to delete. Resources created outside Helm — anything in crds/, or manually-created secrets — are not touched.
Practical Tips
- Run
helm lintin CI on every PR that touches a chart. - Render and snapshot the output (e.g.,
helm-unittest) — catches accidental template breakage. - Pin chart versions in your release pipelines; never
helm installfrommain. - For GitOps with Argo CD: let the controller manage state; never run
helm upgrademanually against a managed namespace.