Skip to content
8 min read·Lesson 4 of 8

Install, Upgrade, and Rollback Releases

The release lifecycle: install, upgrade, rollback, history, diff, atomic, and the trade-offs of each.

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-namespace creates 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 -f and --set are 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:

  1. helm history my-app -n web — find the last deployed revision
  2. helm rollback my-app <last-deployed> -n web
  3. 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 lint in 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 install from main.
  • For GitOps with Argo CD: let the controller manage state; never run helm upgrade manually against a managed namespace.

Key Takeaways

  • helm install creates a release; helm upgrade modifies it; helm rollback restores a previous revision.
  • Use --atomic to make failed upgrades automatically roll back.
  • helm-diff is essential — never upgrade production blind.
  • Release history is stored in cluster secrets named sh.helm.release.v1.<release>.v<n>.
  • helm get all <release> reproduces the rendered manifests and values for any past revision.

Test your knowledge

Try exam-style practice questions to reinforce what you've learned.

Practice Questions →