Lesson 4 · Helm fundamentals

Values & overrides

The precedence rules that make one chart serve every environment and every customer.

Your win: state the values precedence order (defaults → -f files → --set), explain global values, and read this repo's env×org layering — the mechanism behind Course 1's matrix.

Where values come from

A template reads .Values, but that object is merged from several sources. The precedence, lowest to highest:1

subchart values.yaml (lowest) ▼ overridden by the chart's own values.yaml ▼ overridden by -f file1.yaml -f file2.yaml (each -f wins over the last) ▼ overridden by --set key=value (highest — beats everything)

Two rules that trip people up: with multiple -f files, the last one wins; and merging is a deep merge for maps but lists are replaced wholesale, not concatenated.1

Global values — the one shared namespace

A subchart can't read its parent's values — except .Values.global, a reserved section readable by the parent and every subchart alike.2 It's how you set something once (an image tag, an environment name) and have it reach everywhere.

Anchor — the four-layer values stack Each service release layers four values files, in this precedence order (last wins), from skaffold.backend.yaml:22-25 / backend/bob/skaffold.yaml:11-15:
  1. gateway {env}-{org}-values.yaml
  2. backend/values.yaml — the shared global: block (image, VPA, Hasura defaults)
  3. backend/{env}-{org}-values.yaml — cross-service, per-cell
  4. backend/<svc>/{env}-{org}-values.yaml — the most specific, wins
So backend/bob/local-manabie-values.yaml (:42-45) only needs to hold the deltas for that cell — trimmed cpu/memory, a metrics toggle — because everything else comes from the layers below. Same chart, ~30 cells, tiny per-cell files. That's the env×org matrix from Course 1, in Helm.
Where env & org actually get set Skaffold also injects two values directly via setValueTemplates (skaffold.backend.yaml:27-31): global.environment: "{{.ENV}}" and global.vendor: "{{.ORG}}" (plus the image tag). Those are --set-style overrides — highest precedence — and the templates read them back through helpers util.environment / util.vendor (libs/util/templates/_env.tpl:6-16,37-47), which fall back from .Values.global.environment to .Values.environment. That's the exact wiring that turns "ENV=local ORG=manabie" into the right config everywhere.
values.yaml precedence, via Skaffold You rarely type helm -f … -f … here — Skaffold's valuesFiles: list is that ordered set of -f flags. Reordering the list changes who wins. Same Helm rule, expressed in the Skaffold release config.
Backend use case Backend use case: this is the actual explanation for the gateway env-org file, shared backend global values, backend env-org values, and per-service env-org values all being combined in order.
Common mistake Common mistake: remembering that values are layered but forgetting that later files override earlier ones.
Read this next

Helm docs — Values Files + Subcharts & Global Values

Where values come from, the precedence/merge rules, and how global works.

helm.sh — Values Files
helm.sh — Subcharts & Global Values

In plain English Plain English: values are Helm’s configuration inputs, and precedence decides which layer wins when several files set the same field.

Check yourself (from memory)

Q1. Which wins when the same key is set several ways?

Lowest → highest: values.yaml → -f files (last wins) → --set. More specific overrides more general.

Q2. Which values can a subchart read from its parent?

global is the one shared section; otherwise a subchart can't see the parent's values.

Q3. This repo's per-cell {env}-{org}-values.yaml files are small because…

The shared global + defaults layers supply the rest; the per-cell file overrides just what differs (cpu, toggles).
Values precedence, global values, and this repo's env×org layering.
recall, then click to reveal
PRECEDENCE (low→high): subchart values.yaml → chart values.yaml → -f files (LAST wins) → --set (wins all). Maps DEEP-MERGE; lists are REPLACED wholesale. GLOBAL: .Values.global is the one section shared into subcharts (set once, read everywhere). REPO: four layered values files per release (Skaffold valuesFiles, last wins): gateway {env}-{org} → backend/values.yaml (shared global) → backend/{env}-{org}backend/<svc>/{env}-{org} (most specific). Per-cell files hold only DELTAS. env/org injected via setValueTemplates (global.environment/vendor), read via util.environment/util.vendor — Course 1's env×org matrix in Helm.
✅ Part 1 complete You have the fundamentals: what Helm is (L1), a chart's anatomy (L2), the template engine (L3), and how values layer (L4). Part 2 gets to the interesting part — this repo's library-chart architecture: named templates, the copied libs/util chart, hooks, and how Skaffold drives releases.
Ready for Part 2 (named templates & the library chart)? Or a mock interview on Part 1 — values precedence is a classic question. Ask me.

1. Helm — Values Files.

2. Helm — Subcharts and Global Values.