DevTools Hub

Search tools

Search for a developer tool

Helm Values Explained

Part of the Kubernetes Toolkit

A Helm chart's templates reference .Values.something everywhere — .Values.image.tag, .Values.replicaCount — and values.yaml is where those somethings come from. What actually trips people up isn't the concept, it's what happens once more than one values file enters the picture: every claim below was checked against a real Helm install (v3.16.3, helm template), not assumed from the docs.

Layering: -f flags apply in order, later wins

helm install myapp . -f values.yaml -f values-prod.yaml applies values.yaml first, then layers values-prod.yaml on top — anything the second file sets overrides the first. This is a merge, not a replacement of the whole file: fields the later file doesn't mention are left alone.

Maps merge key by key — arrays don't merge at all

This is the distinction that catches people. Given:

# values.yaml
image:
  repository: myapp
  tag: "1.25"
service:
  ports:
    - 80
    - 443

# values-prod.yaml
image:
  tag: "1.26"
service:
  ports:
    - 8080

The merged image ends up with both repository: myapp (from the first file, untouched) and tag: "1.26" (from the second) — maps merge field by field, recursively. service.ports, verified directly, ends up as just [8080] — not [80, 443, 8080], and not [8080, 443]. The second file's array replaces the first's entirely. Assuming arrays merge the same way maps do is the single most common wrong assumption about Helm values.

null doesn't clear a value — it deletes the key

# values-prod.yaml
image:
  pullPolicy: null

Verified directly: this removes pullPolicy from the merged result entirely, as if it had never been set in the first file either — not the same as setting it to an empty string or an actual null value a template would see. If a chart's template has its own default fallback for that field (a common pattern: {{ .Values.image.pullPolicy | default "IfNotPresent" }}), deleting the key this way is exactly how you fall back to it.

--set always outranks -f — regardless of where it appears

Verified both ways directly: --set image.tag=x -f override.yaml and -f override.yaml --set image.tag=x resolve identically, with --set winning either time. Command-line position doesn't matter — Helm's precedence order is fixed (chart's own values.yaml, then -f files in the order given, then --set, then --set-string, then --set-file), not determined by where each flag sits in the command.

The YAML 1.1 gotchas: booleans and octal numbers

Helm parses values with YAML 1.1 semantics (via sigs.k8s.io/yaml). Two consequences worth knowing before they surprise you, both verified directly:

  • Unquoted yes/no/on/off become real booleans. debug: no becomes false, not the string "no". This is the "Norway problem" by name — country: NO (Norway's ISO code) becomes false too.
  • Leading-zero numbers are read as octal. replicaCount: 010 becomes 8, not 10 — a real risk anywhere a value looks like it could be a permission string or a padded number. A plain unquoted tag: 1.20 also silently loses its trailing zero, becoming the number 1.2.

The fix in every case is the same: quote it. debug: "no" and tag: "1.20" stay exactly the text you wrote.

Reading back what's actually deployed

Two different commands answer two different questions, verified from Helm's own --help output: helm show values <chart> shows a chart's own default values.yaml before any overrides — useful for seeing what's configurable in the first place. helm get values <release> shows what an already-installed release is running with, but by default only the values you explicitly supplied — not merged with the chart's defaults. Add -a/--all to get the full computed result, defaults included. Forgetting -a and assuming the output is the complete picture is an easy way to misdiagnose a running release's configuration.

Try it yourself

Helm Values Viewer merges as many values files as you paste, exactly the way described above, and flags every gotcha in this post — the deleted keys, the replaced arrays, the boolean and octal coercions — directly on the result. Helm Values Diff takes two separately-merged configurations (a staging stack and a production stack, for instance) and shows exactly what's different between them. Both run entirely in your browser.

Related tools