skip to content

Which changes to a published chart's values.yaml are breaking for consumers, and how do you ship one?

level: seniorimportance: must knowfreq 58%

answer

  1. Ask what an unchanged consumer file does
  2. Three outcomes, only one is loud
  3. Unknown keys are carried and ignored
  4. Renamed keys change nothing and everything
  5. Make the removed key abort the render

basics

~20 s

A values change is breaking when an unchanged consumer values file now fails to render, silently renders something different, or forces a destructive upgrade. Ship one in a major chart version, with a template that fails loudly on the removed key during a migration window.

solid answer

~40 s

`values.yaml` is the chart's public interface, and the test is what happens to a consumer's existing file when they upgrade without touching it. Three outcomes count as breaking: the render **fails** (a key is now required, or a schema rejects the file); the render **silently differs** because their key is no longer read, so your default applies; or the upgrade is **destructive** because the rendered object names or immutable fields changed. The silent case is the dangerous one — Helm carries unknown values keys in `.Values` and ignores them, so a rename produces no warning at all. Ship the change on a major chart version bump, and during a deprecation window template a `fail` when the removed key is still set, so a silent misconfiguration becomes a failed upgrade with a migration message.

code

yaml · 5 lines
yaml
{{- if .Values.smtpPassword }}
{{- fail "values key 'smtpPassword' moved to 'credentials.smtp.password' in chart 3.0.0" }}
{{- end }}
stringData:
  smtp-password: {{ required "credentials.smtp.password is required" .Values.credentials.smtp.password | quote }}

go deeper

for a junior

Know that values.yaml is what consumers write against, and that renaming or removing a key affects everyone who set it. Bump the chart version whenever you change what the chart renders.

for a middle

Explain the three failure shapes — render error, silent difference, destructive upgrade — and why an unknown values key produces no warning at all from Helm.

for a senior

Demonstrate the migration craft: a major bump, a fail on the removed key, a bridge with a closing date, a rendered diff against real consumer values files before publishing.

for a principal

Own the cost side: each breaking values change is a migration for every consuming team, so batch them, budget how often a major ships, and decide who is accountable for the migration note.

## The contract you are breaking A published chart's `values.yaml` is an API. Consumers write their own file against it, pin a chart version, and then upgrade. Whether a change is breaking has nothing to do with how large the diff is; it is decided by one question: **if a consumer upgrades with their existing values file unchanged, what happens?** Three answers are breaking. **1. It fails.** A key that used to be optional is now required, a type changed from a string to a list, or the chart gained a `values.schema.json` that rejects a file that used to work. This is the *best* kind of breaking change, because the consumer finds out at render time and nothing reaches the cluster. **2. It silently renders something different.** This is the one that causes incidents. Consider an `invoice-worker` chart that renders a Secret from a values key, and version 3.0.0 moves `smtpPassword` to `credentials.smtp.password`. Helm does not validate the shape of user-supplied values: unknown keys are merged into `.Values` and simply never read. So the consumer's `smtpPassword: ...` sits there being ignored, the Secret renders from the chart's default — empty or a placeholder — and the worker restarts with credentials that do not work. Nothing warned anybody. The same shape occurs when you change a *default*: flipping a default replica count, enabling a probe, or tightening a resource limit changes rendered output for every consumer who never set that key. **3. It forces a destructive upgrade.** Values that feed resource names or selector labels are load-bearing in a way ordinary values are not. Change the naming template and the upgrade deletes the old object and creates a new one — a fresh Deployment, an orphaned PersistentVolumeClaim, a new Service with a new cluster IP. Change something that lands in an immutable field, such as a Deployment's selector, and the API server rejects the update outright, leaving the release failed. By contrast, the safely non-breaking change is narrow: **adding a new optional key whose default reproduces the current rendered output exactly.** If the new key changes what an untouched install renders, it is not additive. ## Shipping one anyway Sometimes the rename is right. The technique has four parts. **Bump the major.** The chart version is the only signal consumers have. A breaking values change goes out as 3.0.0, never as 2.97.0, so anyone pinned to a range within 2.x is untouched until they choose to move. **Make the old key loud.** During the window where both are documented, read the removed key and abort with a message that names the replacement: ```yaml {{- if .Values.smtpPassword }} {{- fail "values key 'smtpPassword' moved to 'credentials.smtp.password' in chart 3.0.0" }} {{- end }} stringData: smtp-password: {{ required "credentials.smtp.password is required" .Values.credentials.smtp.password | quote }} ``` `fail` and `required` are both functions Helm adds on top of the standard template library. This converts the silent case into the failing case, which is the whole point — a failed `helm upgrade` at 09:00 is cheaper than a worker running with a broken credential. **Or bridge, then remove.** If a hard failure is too aggressive for the number of teams involved, accept both for one minor line — `{{ .Values.credentials.smtp.password | default .Values.smtpPassword }}` — and emit a deprecation warning through `NOTES.txt`, which prints on every install and upgrade. Then delete the bridge in the next major. The window is only useful if it actually closes. **Write it down where they will look.** The chart's README and `NOTES.txt` carry the migration; a values key rename with no upgrade note is a support ticket per consuming team. ## Verifying before you publish Before tagging the major, render the chart against the values files you know consumers use and diff the output against the previous version. Unexpected differences in object names, selector labels or Secret contents are exactly the class of change described above, and they are visible from a render alone — no cluster required. ## The judgement an interviewer is listening for A strong answer says the quiet part: the reason renames are expensive in Helm is that there is no compiler and no type check between your chart and someone else's values file. Nothing links them except a version number and a document. That is why the major bump, the loud failure and the written migration are not ceremony — they are the only mechanisms available.

  • Is changing a default value in values.yaml a breaking change?
    Often, yes. Every consumer who never set that key gets the new value on their next upgrade, so the rendered output changes for them without any action of theirs. Flipping a default replica count or enabling a feature is at minimum a minor bump with a prominent note, and it is genuinely breaking when it restarts workloads or changes an object in a way the upgrade cannot do in place.
  • Why does Helm not warn about a values key that no template reads?
    Because user-supplied values are merged into `.Values` as free-form data — there is no declared set of legal keys, so an unread key is indistinguishable from one a subchart or a conditional branch might consume. A chart can opt into strictness with a values schema that rejects unknown properties, which converts the typo class of error into a render-time failure.
  • A rename must ship, and seventeen teams consume the chart. What do you do before publishing 3.0.0?
    Publish the migration note first, with a before-and-after values snippet. Render each team's values file against the new chart and check the diff for object-name and Secret changes. Ship a release candidate that a couple of teams opt into. Then publish 3.0.0 with the removed key wired to a `fail` message, so a team that missed the note gets a failed upgrade rather than a broken workload.
  • Which values-driven change turns an upgrade destructive rather than merely different?
    Anything that reaches an object's name or its immutable fields. A changed name template makes Helm create a new object and delete the old one, which loses whatever was attached to it; a changed selector on a Deployment is rejected by the API server and leaves the release failed. Both are visible in a rendered diff before publishing, which is why that diff is the check that matters.

saying these in an interview costs you the question

  • Only calling a change breaking if the render fails
  • Assuming Helm warns about unrecognised values keys
  • Shipping a values rename in a patch release
  • Keeping a compatibility bridge forever with no removal
  • Ignoring changes to rendered object names
  • Treating a changed default as always additive

context