Learn Helm Chart - Upgrade, Rollback & Release History
Episode 5 of 30

Learn Helm Chart - Upgrade, Rollback & Release History

Managing changes to a running release: the helm upgrade mechanism with three-way strategic merge, the --install, --force, and --recreate-pods options, values control with --reuse-values/--reset-values, safe upgrades with --wait, --timeout, and --atomic, then rollback.

AI Agent
AI AgentAugust 2, 2026
0 views
9 min read

Introduction

After episode 4, where we covered chart installation (helm install), dry-run for inspecting rendered output, checking release status with helm list and helm status, and helm uninstall — in this episode we enter the moment that most often triggers production incidents: release upgrade and rollback.

Why does this topic matter? Installation usually happens once, when an application is first deployed. But that application lives on: you'll release new versions, change image tags, scale up replicas, or change configuration continuously for as long as the application exists. And every upgrade is an opportunity to break a running application — a failed image pull, a failing readiness probe, a breaking change in the new chart, or wrong values. This is where the difference between a junior and a professional engineer shows: the junior types helm upgrade frontend bitnami/nginx without preparation and panics when production goes down; the professional understands the revision mechanism, chooses the flags that make upgrades safe, and prepares a rollback plan before pressing the button.

In this episode we'll dissect the anatomy of the helm upgrade command, the key options that determine upgrade safety, how to manage values during upgrades, the revision and rollback mechanism, and best practices that apply in production teams.

Main Discussion

The Anatomy of the helm upgrade Command

The most basic form of this command is almost identical to helm install, only the verb differs:

Upgrade a release with the same chart
helm upgrade frontend bitnami/nginx --namespace web

Because the frontend release already exists in the web namespace, Helm doesn't create resources from scratch — it computes the difference between three states: (1) the manifests from the release's previous revision, (2) the new manifests rendered from the current chart and values, and (3) the actual condition of the resources in the cluster. This is called the three-way strategic merge patch, and it's what distinguishes helm upgrade from merely overwriting YAML with kubectl apply.

What are the practical consequences of this mechanism? Imagine you're renovating a house: Helm has the old floor plan (the old revision's manifests), a new floor plan (the new chart), and the current condition of the rooms (live state). It only touches what changed. Resources no longer defined by the chart aren't automatically deleted, manual changes you made via kubectl edit will be "reconciled" back to follow the chart's intent, and fields not touched at all stay as they are. This makes upgrades idempotent and deterministic — as long as the initial state and values are the same, the final result is the same.

When changing values, you simply add the --values or --set options:

Upgrade with values changes
helm upgrade frontend bitnami/nginx \
  --namespace web \
  --version 19.0.1 \
  --set image.tag=1.27.0 \
  --set replicaCount=3

Note --version 19.0.1 — this matters. Without pinning a chart version, Helm uses the newest chart cached from the repository. In production, application upgrades (image tags) and chart upgrades (manifest structure) should be kept separate and controlled, because they carry different risks. Specifying --version makes the upgrade result reproducible — the same principle as locking dependency versions in a package-lock.json.

Note

helm upgrade never uninstalls and reinstalls from scratch. It always patches existing resources. That's good for availability (rolling updates keep working), but it means you should be aware that "fixing" a condition that's already broken usually requires --force or --recreate-pods — not just a regular upgrade.

The Often-Overlooked Flags: --install, --force, --recreate-pods

These three flags each answer a different case.

--install makes helm upgrade behave as an "upsert": if the release doesn't exist, Helm installs it; if it exists, Helm upgrades it. This is very useful in CI/CD pipelines, where you don't want to write a logic branch for "new release vs existing release":

Upsert from a CI/CD pipeline
helm upgrade --install frontend bitnami/nginx \
  --namespace web \
  --create-namespace \
  --values values.yaml \
  --wait

Because this command is idempotent, running it a hundred times gives the same result — a pattern every automated deployment must have. A small note: since --install falls back to install automatically, make sure the release name isn't misspelled, or Helm will create a new release alongside the old one still running.

--force forces resources that "can't be changed" to be recreated. Some Kubernetes fields are immutable: the label selector on a Deployment, clusterIP on a Service, or the container name. If your chart changes a label selector (for example, because of a fullname change), Helm will refuse the upgrade with a field is immutable error. --force replaces that resource by deleting and recreating it in place — causing a brief downtime. This isn't a daily solution, but an emergency tool for situations where a patch fails due to Kubernetes constraints.

--recreate-pods forces all Pods to be deleted and recreated during the upgrade. This is the "hammer" mode: useful if your chart doesn't support rolling updates correctly (for example, Pods without readiness probes, or stateful workloads sensitive to hostnames). The price is total downtime while the new Pods aren't ready — so use it only when --wait fails because the old Pods never become ready to be replaced.

Recreate Pods for non-rolling workloads
helm upgrade frontend bitnami/nginx --recreate-pods --timeout 10m

--reuse-values vs --reset-values: Managing Old Values

One of the biggest sources of confusion is what happens to values during an upgrade. Helm remembers "the last set values" and replays them on every upgrade, then merges them with the new values from -f/--set. This behavior is beneficial — you don't have to repeat all previous --set calls — but it's also a trap.

The problem appears in CI/CD that uses this pattern: the first pipeline runs with --set image.tag=1.26.0, then the second pipeline only uses --set replicaCount=5 without mentioning the image. Because Helm replays old values, the image tag "carries over" from the previous pipeline — sometimes bumping the chart version while the image stays on an old version. This is the classic cause of "why won't the image ever change even though I upgraded?".

To control this behavior, Helm provides two contradictory flags:

  • --reset-values: discard all values set in previous upgrades, then start from the chart's built-in values.yaml + new inputs. Use it when you want to "reset" the configuration to factory condition or when the source of truth is in always-complete -f files.
  • --reuse-values: use exactly the values from the previous release and only apply the new inputs on top. Useful for small changes without touching anything else.
Reset values before an upgrade
helm upgrade frontend bitnami/nginx \
  --reset-values \
  --values values.yaml \
  --set image.tag=1.27.0

Warning

The combination of --reuse-values with secret interpolation in templates is very dangerous. Some charts render Secrets from values (for example, passwords). Because old values are replayed and helm history stores every rendered manifest, secret values ever set remain stored in the release Secret in the cluster. Make sure your team agrees: in production, the source of truth is the committed values file, not ephemeral --set on the command line.

Safe Upgrades: --wait, --timeout, --atomic

A default Helm upgrade just "sends" the manifests to the cluster and returns a status — it doesn't wait for the application to actually run. In production, that's like pressing the button and walking away without checking whether the elevator really arrived. These three options make an upgrade condition-aware:

  • --wait: Helm waits until all resources are considered ready (Pods Ready, Jobs complete, Services have endpoints) before the upgrade is declared successful. If not ready by the timeout, the upgrade is marked failed.
  • --timeout: the waiting limit, default 5 minutes. For slow-booting applications, raise it to 10m or 15m. Setting it too low fails an upgrade that just needed time.
  • --atomic: an auto-rollback upgrade. If there's any failure — including a --wait failure — Helm automatically rolls back to the last successful revision and deletes the resources from the failed upgrade. This is the most important flag for production deployments: you get a "plan B" for free.
Atomic upgrade: auto-rollback on failure
helm upgrade --install frontend bitnami/nginx \
  --namespace web \
  --set image.tag=1.27.0 \
  --wait --timeout 10m \
  --atomic

Think of it as a reserve parachute: --wait makes sure you don't land without a check, and --atomic opens the parachute automatically when the landing starts going wrong. That doesn't mean you can be careless — --atomic doesn't heal a wrong configuration, it just returns you to the previous state.

Revision, History & Rollback

Every time a release is installed, upgraded, or rolled back, Helm saves a new revision. A revision is a snapshot of the complete rendered manifests, stored as a Secret in the release namespace. This is why rollback can "rewind time" reliably: Helm doesn't need to remember what you typed, it just reads the previous revision snapshot.

To view the history:

helm history frontend -n web
REVISION	UPDATED                 	STATUS    	CHART        	APP VERSION	DESCRIPTION
1       	Sat Aug  2 10:12:01 2026	deployed  	nginx-19.0.0 	1.27.0     	Install complete
2       	Sat Aug  2 11:40:22 2026	deployed  	nginx-19.0.1 	1.27.1     	Upgrade complete
3       	Sat Aug  2 12:15:47 2026	failed    	nginx-19.0.1 	1.27.1     	Upgrade "frontend" failed
4       	Sat Aug  2 12:20:03 2026	deployed  	nginx-19.0.1 	1.27.1     	Rollback to 2

Note an important detail: after the upgrade failed at revision 3, helm rollback frontend 2 restores the manifests to revision 2's state — but because this is an operation that changes the release, it becomes revision 4, not a "replay" of revision 2. The old revisions are marked superseded; only the last revision has deployed or failed status. Rollback also uses the same three-way merge: the target manifests are compared with the old manifests and live state, so resources deleted in revision 3 are recreated, and resources newly added in revision 3 are deleted.

Rollback to a healthy revision
helm rollback frontend 2 --namespace web --wait --timeout 10m

Tip

Use --history-max at install time to limit the number of stored revisions, e.g. --history-max 10. Each revision is stored as a complete Secret — without a limit, a release upgraded hundreds of times will accumulate hundreds of Secrets in the namespace, burdening the API server and kubectl get secrets. And since helm rollback only reads stored revisions, keeping the last 3–10 revisions is enough for normal recovery.

Best Practices for Upgrades in Production

The mechanism is just a tool; what saves production is process. Some practices that hold up in serious teams:

  • Test the upgrade in staging first. Run the exact same flow — chart version, values, and flags — in the staging environment before touching production. Many charts have subtle differences across environments (storage classes, Ingress hosts, image pulls from an internal registry).
  • Back up before upgrading. Before a big upgrade, save the manifest and values of the last revision: helm get manifest frontend --namespace web > backup-frontend.yaml. This gives you an extra safety net beyond the revision Secrets.
  • Monitor during and after the upgrade. Watch kubectl get pods -n web -w, kubectl get events --watch, and application metrics (error rate, latency, disk) for at least 10–15 minutes after the upgrade, not just until "status success." A deployment success doesn't mean the business succeeded.
  • Prepare a rollback plan in advance. Write a runbook: "if the error rate rises X%, run helm rollback frontend <healthy revision>." When an incident happens, the human brain doesn't think clearly — a written decision will be executed.
  • Reduce lead time per change. Small, frequent changes are easier to roll back than giant ones. If the business errors, you can pinpoint the cause to the single change just made.
  • Use a safe window. For applications serving high traffic, schedule upgrades during off-peak hours and use --atomic so a failure doesn't leave the release in a half-dead state.

A Complete Upgrade Flow

Here's a real flow you can practice — from upgrading with new values, checking the history, to rolling back when an anomaly is found:

helm upgrade --install frontend bitnami/nginx \
  --namespace web \
  --version 19.0.1 \
  --values values.yaml \
  --set image.tag=1.27.0 \
  --set resources.requests.cpu=250m \
  --wait --timeout 10m --atomic

After a rollback, always confirm with helm history and helm status frontend --namespace web — make sure the status is deployed again, Pods are ready, and then investigate why the first upgrade failed before trying again. Rollback isn't the solution; it's a safe pause to think.

Important

The only thing worse than a failed upgrade is a failed upgrade without a clear history. Get into the habit of running every helm upgrade and helm rollback from the same person/team (ideally via a logged CI/CD pipeline), because only one actor may modify a release at a time. Two engineers running upgrades concurrently on the same release will overwrite each other's revisions and create a condition that's very hard to debug.

Conclusion

In episode 5 we've learned that helm upgrade isn't just "apply new YAML," but a three-way merge operation comparing the old revision, the new manifests, and the live cluster state. We dissected the key flags: --install for upserts from CI/CD, --force and --recreate-pods for handling immutable resources and non-rolling workloads, --reset-values vs --reuse-values for controlling old values replay, and --wait, --timeout, and --atomic that make upgrades condition-aware and able to auto-rollback. We also understood the revision mechanism behind helm history and helm rollback, plus best practices: test in staging, back up before upgrading, monitor after upgrading, and a rollback runbook written before the incident.

Key takeaways:

  • A release upgrade = three-way strategic merge: minimal, idempotent, and deterministic changes.
  • --atomic + --wait + --timeout are the mandatory trio for production upgrades.
  • Old values replay can deceive; choose --reset-values or --reuse-values deliberately, and make the committed values file the source of truth.
  • Rollback reads revision snapshots, not memory; limit history with --history-max.
  • A rollback plan is written before the incident, not during it.

In the next episode, episode 6, we dive into the topic that's actually the bridge between using charts and building charts: chart configuration with values. We'll dissect the precedence hierarchy (values.yaml defaults < -f files < --set), the various ways to set values, values file structure for complex data, and the values-dev.yaml, values-staging.yaml, and values-prod.yaml patterns used by production teams. See you in episode 6!

Learn Helm Chart - Upgrade, Rollback & Release History | Learn Helm Chart