skip to content

Releases & Upgrades

A release is the installed instance of a chart, its revisions stored as Secrets in the namespace. This is the state machine: how Helm writes to the cluster, what it waits for, what it rolls back to, and what it leaves behind. Upgrade failures are operational reality.

part ofHelmoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

A `helm upgrade` fails with `field is immutable` — what happened, and what state is the release left in?

level: juniorimportance: must knowfreq 64%

answer

  1. The refusal comes from the cluster, not Helm
  2. Some object fields are write-once
  3. Selector, Job pod template, volumeClaimTemplates
  4. A failed upgrade is still a stored revision
  5. Patching cannot help; replacing can

basics

~20 s

The Kubernetes API server rejected the write because the chart's new manifest changes a field that cannot change after the object was created — a Deployment's spec.selector is the classic one. Helm records the attempt as a failed revision and the live object keeps its old spec.

solid answer

~40 s

That message comes from the API server, not from Helm. `helm upgrade` renders the chart, sorts the objects by kind and sends them one at a time; when it reaches an object whose new spec changes a write-once field, the server refuses it and the upgrade stops. The three you actually meet are a workload's `spec.selector`, a Job's pod template, and a StatefulSet's `volumeClaimTemplates`. The release is now partly applied: objects Helm wrote before the rejected one are live, the rejected object is untouched, and Helm has stored a **new revision with status `failed`** while the previous revision is still the deployed one. Nothing is undone unless the upgrade ran with `--rollback-on-failure`. Getting out means changing the chart so the field stays put, or accepting that the object has to be deleted and recreated.

go deeper

for a junior

Be ready to say plainly that the cluster refused the write, name one field it happens on, and state that the old object is still running. Knowing that Helm stored a failed revision and changed nothing on the rejected object is the whole answer at this level.

for a middle

Explain the mechanics: Helm renders, sorts by kind, applies one object at a time and stops at the first hard failure, so the release ends up partly applied with a failed revision on top of the last deployed one. Name the three fields this bites on.

for a senior

An interviewer expects you to move from the message to a plan — revert the field, recreate the object, or rename it — and to say what each costs in availability. Mention checking the partial state of the release before you decide, rather than re-running the command.

for a principal

Own the prevention angle: which chart changes are allowed to touch a selector, whether a server-side dry run is a required check before a chart bump reaches a cluster, and who is allowed to authorise an object recreation in production.

### The refusal is the cluster's, not Helm's Helm has no notion of which fields may change. `helm upgrade` renders the chart, sorts the rendered objects into Helm's fixed kind order, and writes each one to the Kubernetes API server. The server validates every update, and for a set of fields it accepts only the value written when the object was created. The refusal text — `field is immutable` — is the server's, and Helm surfaces it under a failed upgrade. That is why no combination of Helm flags makes an ordinary update of such a field succeed: the write is a legal Helm operation and an illegal Kubernetes one. Anything Helm can do about it is about replacing the object, not about persuading the server. ### The three you will actually meet **A workload's `spec.selector`.** The labels a Deployment or StatefulSet selects on are fixed at creation. In a chart this is rarely somebody hand-editing a selector; it is a helper template feeding it. A third-party ingress-controller chart that moves from 4.11.3 to 4.12.0 may rewrite the label helper the selector renders from, and then every release of that chart is rejected on upgrade. There is a subtler variant worth knowing: charts commonly truncate a generated name to 63 characters, so a release whose name is 71 characters long renders a selector value that is a cut-off string. Change anything that lands before the cut — a prefix, an added component word — and the truncated value changes even though nobody touched the selector block. **A Job's pod template.** A Job that sits in `templates/` without hook annotations is an ordinary release object, so Helm tries to update it on every upgrade. Its pod template cannot change, so the first upgrade that bumps that Job's image is rejected. Charts avoid this by giving the Job a name that changes per release or by moving it out of the ordinary lifecycle entirely. **A StatefulSet's `volumeClaimTemplates`.** Growing a claim, adding a second volume, switching a storage class: all rejected on an existing StatefulSet. ### What state the release is in afterwards Helm applies in kind order and stops at the first hard failure, so the release is partly applied — everything ordered before the rejected object is already live in the cluster. Helm still writes a revision record: `helm history` shows a new revision with status `failed`, and the revision below it, the last successful one, is the one marked deployed. `helm status` reports the release as failed. Helm does not undo the objects it already wrote unless the upgrade was run with `--rollback-on-failure`; without it you are left with a half-new release and a failed revision on top. That matters for the next attempt. Helm computes the next upgrade against the manifest stored with the last deployed revision, which still describes the old shape — so a second run of the same command reproduces the same rejection, usually after re-applying the same partial set. Retrying is not a strategy. ### Getting out There are three honest ways forward. Revert the field so the chart renders the value the object already has — often the right answer when a chart bump changed a selector you never wanted changed, and you can pin the values that feed it. Recreate the object, which is what `helm upgrade --force-replace` does: Helm stops patching and removes and re-creates, so the new value lands and everything the object manages goes down and comes back. Or give the object a new name, so the chart renders a fresh object and the old one is removed as part of the same upgrade — a controlled cutover rather than a delete-and-recreate in place. ### What not to do Do not reach for `--force-conflicts`. It is a different Helm 4 flag that settles field-manager ownership under server-side apply, and immutability is not an ownership question — the server refuses the value no matter who is asking. Do not edit or delete the release record Secret to make the failure go away; the object in the cluster is what is refusing the change, and the record is only Helm's memory of it. And do not conclude from a failed upgrade that nothing was applied. ### Seeing it before it bites A server-side dry run (`helm upgrade --dry-run=server`) sends the rendered objects to the API server for validation without committing them, which is where an immutable-field rejection is cheapest to discover. Rendering the new manifest and diffing it against the manifest stored with the current revision is the other habit: if a selector block or a Job pod template moved, you know before you touch the cluster.

  • Does Helm undo the objects it already applied before the rejection?
    No. Helm writes in kind order and stops at the failure, leaving the earlier objects live. It only reverts automatically if the upgrade ran with `--rollback-on-failure` (`--atomic` is the deprecated alias for that flag). Otherwise you are looking at a partly-applied release, and returning to a known state is a deliberate rollback you run yourself.
  • Which revision does `helm history` show as deployed once the upgrade has failed?
    The previous one. Helm records the attempt as a new revision with status `failed` and leaves the last successful revision marked deployed, so the history shows a failed revision sitting on top of a deployed one. The next upgrade is computed from the deployed revision's stored manifest, which is why simply re-running the same command reproduces the same rejection.
  • How would you have caught this before running the upgrade?
    Run the upgrade as a server-side dry run so the API server validates the rendered objects without committing them; immutable-field rejections surface there. Alongside that, render the new chart version and diff it against the manifest stored with the current revision — a moved selector block or a changed Job pod template is visible in the diff, and that check is cheap enough to sit in CI on every chart bump.

saying these in an interview costs you the question

  • Says Helm itself refuses the change rather than the API server
  • Assumes a failed upgrade rolls itself back automatically
  • Thinks --force-conflicts clears an immutable-field rejection
  • Believes nothing was applied because the upgrade failed
  • Says deleting the release record Secret repairs the object
  • Expects retrying the identical upgrade to eventually succeed

context

open as a page

What does `helm upgrade --install` do, and why do CI pipelines prefer it to `helm install`?

level: juniorimportance: must knowfreq 80%

basics

~20 s

helm upgrade --install upgrades a release when one with that name already exists in the namespace and installs it when none does. Pipelines use it because the same command works on the first deploy and every deploy after it.

open as a page

In what order does Helm apply the resources rendered from a chart's templates/ directory?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Helm sorts every rendered manifest by Kubernetes kind using a fixed, hard-coded list: Namespace first, then config and RBAC, then Service, then workloads, then Ingress. File names under templates/ do not decide the order.

open as a page

Why does helm install fail with "exists and cannot be imported into the current release"?

level: juniorimportance: must knowfreq 70%

basics

~20 s

An object of that kind and name is already in the cluster and does not carry this release's Helm ownership metadata. Helm refuses to overwrite anything it cannot prove belongs to the release being applied.

open as a page

Where does Helm store the record of a release, and what is inside it?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Helm writes one Secret per release revision into the release's own namespace, named sh.helm.release.v1.<name>.v<rev> with type helm.sh/release.v1. It carries the chart, the values supplied, the rendered manifest and the status, as JSON that is gzipped and base64-encoded.

open as a page

What does `helm history` print for a release, and what does each column tell you?

level: juniorimportance: must knowfreq 74%

basics

~20 s

helm history <release> prints one row per stored revision: the revision number, when it was written, its status (deployed, superseded, failed or a pending state), the chart name with its chart version, the app version, and a description.

open as a page

What does helm upgrade --rollback-on-failure do, and how does --atomic relate to it?

level: juniorimportance: must knowfreq 68%

basics

~10 s

--rollback-on-failure makes helm upgrade restore the previous release revision automatically when the upgrade fails, so no half-applied change is left running. In Helm 4 --atomic is a deprecated alias of that same flag.

open as a page

What does helm uninstall remove, and what does the --keep-history flag change?

level: juniorimportance: must knowfreq 74%

basics

~10 s

helm uninstall deletes the Kubernetes objects recorded in the release's stored manifest, then deletes the release records, freeing the name. --keep-history keeps those records instead, so the release stays in history with status uninstalled.

open as a page

What does `helm upgrade --force-replace` actually do to an object, and what does it cost?

level: middleimportance: must knowfreq 52%

basics

~20 s

It stops Helm patching the existing object and has it removed and created again from the rendered manifest, which is how a change to a write-once field such as a selector lands. Everything the object manages is torn down and rebuilt, so it means an outage.

open as a page

After `helm rollback 3` on a release at revision 5, which revision is the release on?

level: middleimportance: must knowfreq 66%

basics

~10 s

Revision 6. helm rollback never rewinds the counter: it appends a new revision holding revision 3's content, marks revision 5 superseded, and leaves 3, 4 and 5 in history with their original numbers.

open as a page

Why does Helm's --rollback-on-failure flag also change whether the upgrade waits?

level: middleimportance: must knowfreq 52%

basics

~20 s

Helm can only roll back failures it has observed. Setting --rollback-on-failure therefore defaults the wait strategy to watcher, so Helm stays until the release is ready; without waiting it would exit successfully before crash-looping pods ever appeared.

open as a page

In Helm 4, what does --server-side default to on helm install versus helm upgrade?

level: middleimportance: must knowfreq 55%

basics

~20 s

Helm 4 installs with server-side apply: on helm install --server-side is a boolean defaulting to true. On helm upgrade and helm rollback it is a string defaulting to auto, which reuses whatever apply method the previous revision used.

open as a page

What does Helm's "another operation (install/upgrade/rollback) is in progress" error mean?

level: middleimportance: must knowfreq 72%

basics

~20 s

It means the release's newest stored revision still has a pending status, so Helm refuses to start a second write. Usually nothing is running: an earlier helm process was killed before it could record a terminal status.

open as a page

In Helm 4, what values does --wait accept, and what does omitting the flag do?

level: middleimportance: must knowfreq 72%

basics

~20 s

In Helm 4 --wait names a strategy, not a boolean: watcher (event-driven, kstatus-based), legacy (Helm 3's polling waiter), or hookOnly. Bare --wait means watcher; omitting it leaves hookOnly, so Helm does not wait for workloads.

open as a page

After helm uninstall, why do a StatefulSet's PVCs and the chart's CRDs remain?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Helm only deletes objects listed in the release's stored manifest. Claims created by the StatefulSet controller were never rendered, so Helm has no record of them, and CRDs installed from a chart's crds/ directory are deleted by Helm never, by design.

open as a page

What do Helm's pending-install, pending-upgrade and pending-rollback release statuses mean?

level: juniorimportance: should knowfreq 55%

basics

~20 s

They are the in-flight statuses Helm writes on a revision before it touches the cluster, one per operation: a first install, an upgrade, a rollback. Seeing one after the operation ended means the revision was never finished.

open as a page

What does Helm's --timeout flag bound on install and upgrade, and what is its default?

level: juniorimportance: should knowfreq 55%

basics

~20 s

--timeout is the budget for the waiting Helm does during a release: hook execution and, when a wait strategy is on, readiness. It takes a Go duration string such as 9m30s and defaults to 5m0s.

open as a page

In Helm 4, `--force-replace` and `--force-conflicts` cannot be combined — what does each one do?

level: middleimportance: should knowfreq 40%

basics

~20 s

--force-replace abandons updating an object and recreates it, which is how a change to a write-once field lands. --force-conflicts keeps the normal apply but overrides another field manager's ownership. Only the first clears an immutable-field failure.

open as a page

In Helm, is a release name unique per cluster or per namespace, and what follows from that?

level: middleimportance: should knowfreq 58%

basics

~20 s

A Helm release name must be unique within its namespace, not across the cluster. The same name in two namespaces is two independent releases, and one chart can be installed twice in one namespace under two different names.

open as a page

When a Helm chart has subcharts, are the parent's resources applied before the subcharts'?

level: middleimportance: should knowfreq 46%

basics

~20 s

No. Helm renders the parent and every subchart into one flat set of manifests and applies the fixed kind sort across all of them together, so a subchart's Service goes before the parent's Deployment. Charts are not sequenced.

open as a page

In Helm, how do you adopt an object that already exists in the cluster?

level: middleimportance: should knowfreq 52%

basics

~20 s

Either put Helm's ownership metadata on the live object by hand — the managed-by label and the two meta.helm.sh annotations — then upgrade; or pass --take-ownership (Helm 3.17), which claims the object and writes that metadata for you.

open as a page

What does Helm's --history-max control, and what does exceeding it remove?

level: middleimportance: should knowfreq 44%

basics

~20 s

It caps how many revision records Helm keeps for one release, defaulting to 10. When an upgrade exceeds the cap, Helm deletes the oldest records, so those revisions leave helm history and can no longer be rolled back to.

open as a page

Does `helm rollback` re-render the chart, and can it work if that chart version is gone?

level: middleimportance: should knowfreq 54%

basics

~20 s

No re-render. Each revision record carries the chart, the supplied values and the fully rendered manifest, and helm rollback re-applies that stored manifest. Nothing is fetched, so a deleted chart version or an unreachable repository does not block a rollback.

open as a page

How does Helm 4's server-side apply differ from Helm 3's client-side three-way merge on upgrade?

level: middleimportance: should knowfreq 38%

basics

~20 s

Helm 3 built the patch itself from three inputs: the manifest stored with the previous revision, the newly rendered manifest, and the live object. Helm 4 sends the whole rendered object and the API server merges it, tracking which manager owns each field.

open as a page

What does the helm.sh/resource-policy: keep annotation do to a rendered resource?

level: middleimportance: should knowfreq 54%

basics

~20 s

helm.sh/resource-policy: keep tells Helm to skip deleting that object when an operation would otherwise remove it — uninstall, or an upgrade that drops it from the chart. The object survives as an orphan Helm no longer manages.

open as a page

`helm upgrade` is rejected because a StatefulSet's volumeClaimTemplates changed — how do you ship it?

level: seniorimportance: should knowfreq 34%

basics

~20 s

The StatefulSet object has to be recreated, so the decision is how. --force-replace takes every pod down at once; removing the object out-of-band while leaving its pods running lets the next upgrade recreate it with no gap; renaming it makes a fresh object and needs a data migration.

open as a page

Why can installing one Helm chart a second time under a new release name still fail on cluster-scoped objects?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Release names are unique per namespace, but ClusterRoles, CustomResourceDefinitions and webhook configurations have one cluster-wide name. A second release of a chart that renders them collides on objects the first release already owns, whatever namespaces the two live in.

open as a page

Why does a Job under a Helm chart's templates/ apply after the chart's Deployments?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Job sits near the end of Helm's hard-coded install-order table, behind Deployment and StatefulSet, and nothing in a chart can move it. Even if it sorted first, Helm submits manifests without waiting, so the Job would still not be finished.

open as a page

Why is helm upgrade --take-ownership risky, and how do you limit its blast radius?

level: seniorimportance: should knowfreq 38%

basics

~20 s

It turns off Helm's ownership check for the whole run, so the release claims every object it collides with — including ones another release owns — and each claimed object is then deleted by a later uninstall. Stamp the specific objects instead.

open as a page

A helm upgrade fails saying the release Secret is too long. What is Helm storing there, and how do you fix it?

level: seniorimportance: should knowfreq 33%

basics

~20 s

The record embeds the whole chart plus supplied values and rendered manifest, gzipped into one Secret the API server rejects past roughly 1 MiB. Shrink what the chart carries, split the release, or change backend; trimming history will not help.

open as a page

showing 1–30 of 46