Helm: a values file instead of --set
The order values are layered in, how several files get merged, and the rules Helm uses to merge dicts and lists.
Why
The same chart gets installed into several environments: stage, prod, a
local cluster. They differ in settings: replica count, image tag, Ingress
hosts. Keeping those differences in a long --set command is awkward,
hard to repeat, and can't go into git. A values file solves both problems.
Concepts
Every chart has a values.yaml with default settings. You override them
two ways: the --set flag right in the command, or a separate file via
-f (long form --values).
A values file is plain YAML with the same structure as the chart's
values.yaml. Put into it only what differs from the default:
# myvalues.yaml
replicaCount: 2
image:
repository: nginx
tag: "1.27.3-alpine"
resources:
limits:
memory: 256Mi
The file is passed on install or upgrade:
helm install web ./myapp -f myvalues.yaml -n demo
To see which keys a chart even understands, check its defaults:
helm show values ./myapp
How it works
Order of priority
Values are layered, and each next layer overrides the previous one:
values.yamlinside the chart.- Files from
-f, left to right. --set,--set-string,--set-file.
That gives a practical rule: --set beats everything, and among several
-f files the last one wins.
Several files are used to separate what's shared from what's specific:
helm upgrade --install web ./myapp -n demo \
-f values/common.yaml \
-f values/prod.yaml
common.yaml holds what's the same across all environments, prod.yaml
holds only prod's differences. The second file sits on top of the first.
How values merge
Dicts merge deeply. If common.yaml sets image.repository and
prod.yaml sets only image.tag, both keys survive.
Lists get replaced entirely. It's easy to slip here: the
ingress.hosts array from the second file doesn't add to the array from
the first, it replaces it. To get a union, you have to spell out the whole
list in the top file.
You can remove a key set further down the chain by assigning it null:
# prod.yaml
podAnnotations: null
An empty dict {} doesn't work that way. It merges with the dict below by
the usual rule, and every key below stays in place. null removes the key
from the final set of values, and the template sees it as absent.
| What's set | How it layers | Result |
|---|---|---|
| a dict | merges deeply | keys from both files remain |
an empty dict {} | merges deeply | changes nothing |
| a list | gets replaced entirely | the top file replaces the bottom one |
null | removes the key | the template sees the value as absent |
What actually applied
helm get values shows the final set of values for a release:
helm get values web -n demo # overrides only
helm get values web -n demo -a # the full set, including chart defaults
The -a (--all) flag matters when you're troubleshooting an incident.
It shows exactly which value the app is actually running with, and saves
you from guessing across several files. Before installing, you check the
same thing with a render, no cluster needed:
helm template web ./myapp -f values/common.yaml -f values/prod.yaml
A typo in a key name passes silently: replicasCount: 3 just does
nothing. If a values.schema.json sits next to the chart, Helm validates
values against it on install, upgrade, lint and template, but an
extra key is rejected only if the schema forbids it. You can't rely on the
schema. A typo gets caught by comparing helm get values -a with what you
meant to set.
Why a file, if there's --set
--set is good for one or two quick edits on the fly. The rest of the
time, a file wins: it lives in git next to the code, people read it in
full, and you can see its history. The command doesn't turn into a long
tail of flags that nobody can reproduce later.
Common mistakes
A number instead of a string. YAML reads 1.25 without quotes as a
floating-point number. Sometimes that's invisible: tag: 1.25 gives the
image nginx:1.25. But tag: 1.10 turns into nginx:1.1, because the
number has no trailing zero, and 20241001 after quote in the template
becomes "2.0241001e+07". Write tags, versions and anything number-like
in quotes.
Wrong nesting level. Your file has to repeat the structure of the chart's
values.yaml. A value placed one level too high or too low won't raise an
error, it just goes unused.
An empty dict instead of null. resources: {} in the top file doesn't
cancel resources from the file below, the keys below still reach the
template.
Values for a subchart live under its name: redis.auth.password, not
auth.password. This is a common cause of "the setting isn't taking
effect" when working with umbrella charts.
Homework
Practice the theory on your own cluster: 1 step with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.
./run.sh start k8s-helm-values-file # set up the starting state
./run.sh verify k8s-helm-values-file # check the current step
In short
- Layering order: the chart's
values.yaml, then the-ffiles left to right, then--set. - Dicts merge deeply, lists get replaced entirely.
nullremoves a key set at a lower layer;{}doesn't remove it.helm show values <chart>shows the defaults,helm get values <release> -ashows what actually applied.- An unknown key raises no error, so a typo gets caught by comparing
against
helm get values -a. - Subchart values are set under the subchart's name.
What you'll need for the tasks
helm template <release> <chart> -f <file> -f <file>: render manifests with no cluster and see what you get.helm show values <chart>: which keys a chart accepts.helm get values <release> -n <ns> -a: a release's final values in the cluster.kubectl get events -n <ns>:FailedSchedulingevents explain why a Pod wasn't scheduled onto a node.kubectl describe podshows itsNode-Selectors.- Merge rules: dicts deeply, lists entirely,
nullremoves a key.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson k8s-packaging-08-helm-values
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress