skip to content

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%

answer

  1. Ask where the merge is computed
  2. The old path needed three inputs
  3. One of them lived in the release record
  4. The new path tracks who owns each field
  5. Silent overwrite became an explicit conflict

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.

solid answer

~50 s

The difference is where the merge happens and what it knows. On Helm 3's client-side path, Helm computed a three-way strategic merge patch locally from the manifest **stored in the previous release record**, the **newly rendered** manifest, and the **live object**, then sent that patch. The stored manifest was Helm's stand-in for "what I last said", which is why removing a field from a chart could still remove it from the cluster. On Helm 4's server-side path, Helm sends the full rendered object and the API server does the merging, keeping a per-field record of which manager set what. The practical consequences: a field written by another controller or a person is no longer silently overwritten or silently kept, it produces an explicit conflict; and because `--server-side` defaults to `auto` on upgrade, a release created under Helm 3 keeps the old behaviour until you flip it.

code

yaml · 9 lines
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-scoring
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-scoring

go deeper

for a junior

Know the one-line version: Helm 3 worked out the change on your machine and sent a patch; Helm 4 sends the whole object and lets the cluster's API server work out the change.

for a middle

Name the three inputs of the old merge — the manifest stored with the previous revision, the new render, and the live object — and say what the new path adds: a per-field record of which manager owns what.

for a senior

Explain the operational consequence: disagreements that used to appear as silent drift now fail the upgrade with a named conflict, and because upgrade defaults to inheriting the previous method, an estate can run both paths at once.

for a principal

Frame it as an ownership model, not a merge algorithm: decide which fields charts may claim when controllers also write them, and what the org does with conflicts that are really unresolved ownership decisions.

Both paths answer the same question — given the object I want and the object that exists, what do I send to the cluster? — and they answer it in different places, with different information. ## The Helm 3 path: three inputs, merged on the client Helm 3 constructed the patch itself before talking to the cluster, from three inputs. The **old** state was the manifest stored inside the previous release record: literally the YAML Helm rendered last time, kept with the revision. The **new** state was the manifest just rendered from the chart and values. The **live** state was the object fetched from the cluster. From those three, Helm computed a strategic merge patch and sent it. The stored manifest is what made the merge three-way rather than two-way, and it is the part people forget. It let Helm distinguish "this field is absent from the new render because the chart dropped it" — remove it from the object — from "this field is absent because Helm never set it" — leave it alone. That is a real and useful property, and it is why deleting a key from a chart usually removed it from the live object rather than leaving it behind. What the client-side path could not do is know *who else* had touched a field. It had a memory of its own last write and a snapshot of the current object, and nothing about authorship. So a value set by a person or another controller was either overwritten or preserved depending on how the field was structured in the merge, and Helm said nothing either way. Drift was discovered later, by whoever noticed the workload behaving oddly. ## The Helm 4 path: one object, merged by the server Under server-side apply, Helm stops computing patches. It sends the full object it wants, identified as coming from its own field manager, and the API server merges it against the live object while maintaining a record of which manager owns which field. Ownership is the new ingredient. When Helm's apply would change a field owned by somebody else, the server refuses and returns a conflict naming the manager and the fields, and Helm reports the upgrade as failed. `--force-conflicts` re-sends the apply with the override, moving those fields to Helm. Field removal still works, for a different reason: a field Helm previously owned and no longer sends is dropped from the object by the server, because the ownership record says Helm had claimed it. So the useful Helm 3 property survives; the mechanism behind it changed from a stored-manifest comparison to an ownership ledger the API server maintains. ## What changes for you in practice The headline behavioural difference is that a disagreement now surfaces as an error at the moment it happens, rather than as drift discovered a fortnight later. That feels like a regression the first time an upgrade fails on an annotation somebody added by hand during an incident, and it is not: the conflict was always there, it was simply resolved silently. The secondary difference is that Helm is no longer alone in deciding the merge, so a controller that legitimately owns a field can keep it, provided the chart stops claiming it too. ## Two paths in one estate This is not a clean before-and-after. Helm 4's `--server-side` is a boolean defaulting to `true` on install but a string defaulting to `"auto"` on upgrade and rollback, and `auto` inherits the previous revision's method. A release first installed by Helm 3 therefore keeps using the client-side three-way merge under a Helm 4 CLI, indefinitely, until an operator passes `--server-side=true`. So "which merge is this release using" is a per-release fact you look up, not a per-CLI-version fact you assume — and the answer explains why two releases in the same namespace can react differently to the same hand-edit. ## What is unchanged The merge path is only about how the object is written. Helm still stores a release record for every revision with the rendered manifest inside it, `helm history` still lists them, and `helm rollback` still restores from the stored manifest — the stored manifest simply stopped being an *input to the merge* and remained the record of what a revision contained. Release naming, namespacing and revision numbering are untouched by the choice.

  • If Helm 3 merged on the client, why did removing a field from a chart still remove it from the live object?
    Because the merge had three inputs, not two. The manifest stored with the previous revision told Helm what it had set last time, so a field present there and absent from the new render was recognised as a deletion rather than as something Helm had never managed. Under server-side apply the same outcome comes from the ownership record instead: a field Helm owned and stops sending is dropped by the API server.
  • Under server-side apply, what happens to a field a person set with a live edit?
    It is attributed to that person's field manager. If the chart does not render that field, it simply stays. If the chart does render it, Helm's next apply conflicts and the upgrade fails, naming the manager and the field — where the old client-side path would have quietly overwritten or quietly kept the value, with no message either way.

The old path was Helm editing a shared document from memory of its own last draft; the new path is handing the whole page to an editor who knows which paragraph each author wrote.

saying these in an interview costs you the question

  • Says Helm 3 compared only the chart and the live object
  • Cannot say what the third input to the merge was
  • Claims server-side apply cannot delete a removed field
  • Thinks Helm 4 stopped storing the rendered manifest
  • Assumes every Helm 4 upgrade uses server-side apply

context