In a Helm chart, which built-in objects are bound before the templates render?
answer
- Seven names exist before any function runs
- One holds user input, one holds the release
- Capitalized because they are Go struct fields
- Release is shared, Chart is per-chart
- Values, Release, Chart, Files, Template, Subcharts
basics
~10 sHelm binds .Values, .Release, .Chart, .Files, .Capabilities, .Template and, in a parent chart, .Subcharts. .Values holds the merged user values, .Release describes this install, and .Chart mirrors Chart.yaml with capitalized field names.
solid answer
~40 sBefore the first template action runs, Helm builds one root value and hangs seven members off it. `.Values` is the merged result of the chart's `values.yaml` plus whatever the caller passed. `.Release` describes this particular install: `Name`, `Namespace`, `Revision`, `IsInstall`, `IsUpgrade` and `Service`, which is always the string `Helm`. `.Chart` is the parsed `Chart.yaml`, but as Go struct fields, so the key `appVersion` is reachable as `.Chart.AppVersion`. `.Files` reads non-template files packaged with the chart. `.Capabilities` reports what the target cluster supports. `.Template` names the file currently rendering. `.Subcharts` gives a parent read-only access to a dependency's chart and values. All of them are read-only during a render; a template cannot assign to them.
code
yaml · 11 linesmetadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
annotations:
rendered-by: {{ .Template.Name }}go deeper
Be able to list the objects and say what each one holds, and get the spelling right: .Chart.AppVersion, not .Chart.appVersion. Knowing that .Release.Service is always the string Helm is a cheap win.
Explain why the fields are capitalized while .Values keys are not, and the difference between .Chart.Version and .Chart.AppVersion. Be ready to say what a subchart's template sees for each object.
Show that you know which objects are shared across a release and which are per-chart, and that everything derived from them is frozen into the stored manifest at render time rather than re-evaluated later.
Own the convention: which built-ins your organisation's charts are allowed to bake into names and labels, and what that costs when a chart is renamed, re-parented or vendored as a dependency.
### What "built-in object" means A Helm chart's `templates/` directory is not YAML: it is Go text/template source that happens to produce YAML. Before Helm executes the first action in those files, it constructs a single root value and binds it to the dot. Everything a template can reach without calling a function hangs off that root, and the top-level members of it are what Helm calls the *built-in objects*. There are seven, and they are the entire vocabulary a chart starts with. They all begin with a capital letter because they are Go struct fields, and Go only exports capitalized names. That single fact explains the most common beginner error on this topic: the key in `Chart.yaml` is `appVersion`, but the template spells it `.Chart.AppVersion`. `.Values` is the exception you never have to capitalize *into* - the keys underneath it are whatever the values file literally wrote, so `.Values.image.tag` stays lowercase all the way down. ### The seven **`.Values`** is the merged values tree: the chart's own `values.yaml` defaults, overlaid by whatever the caller supplied. What ends up in it is the values-precedence question and belongs to its own topic; from inside a template it is simply a nested map, and a key nobody set is missing rather than empty, which is why `default` and `required` exist. **`.Release`** describes this install. `.Release.Name` is the release name chosen at install time and fixed for the release's life. `.Release.Namespace` is the namespace the operation targets. `.Release.Revision` is the revision being created - 1 on install, one higher than the current revision on upgrade. `.Release.IsInstall` and `.Release.IsUpgrade` say which operation is rendering. `.Release.Service` is always the literal string `Helm`, which is why the conventional label is written `app.kubernetes.io/managed-by: {{ .Release.Service }}` instead of hard-coding it. There is no timestamp member: Helm 3 removed the old `.Release.Time`, and reaching for sprig's `now` instead makes every render produce different output. **`.Chart`** is the parsed `Chart.yaml` - `Name`, `Version`, `AppVersion`, `Type`, `Description`, `Annotations` and the rest. Two of these are routinely confused: `Version` is the chart's own version and `AppVersion` is the version of the software the chart ships, which is why the standard label set uses `{{ .Chart.AppVersion | quote }}` for the app and `{{ .Chart.Name }}-{{ .Chart.Version }}` for the chart. **`.Files`** reaches the non-template files packaged inside the chart - `.Files.Get` for one file's contents, `.Files.Glob` for a set. Files under `templates/` are not visible through it, and neither is anything `.helmignore` excluded from the package. **`.Capabilities`** reports what the target cluster can accept. It exists here for completeness; how you interrogate it is a separate subject. **`.Template`** describes the file being rendered right now: `.Template.Name` is the path of the current template inside the chart, such as `payments-ledger/templates/deployment.yaml`, and `.Template.BasePath` is its directory. It is used almost exclusively for error messages and debug annotations. **`.Subcharts`** lets a parent chart read a dependency's chart object and values, addressed by the dependency's name or alias. It is read-only in that direction only; a subchart has no matching way back up. ### Which of them a subchart sees differently This is the part interviewers actually probe. When Helm renders a chart that has dependencies, it does not render one flat context. `.Release` is the same object everywhere - a single release name, namespace and revision covers the parent and all of its dependencies, which is exactly why every object in the release can be labelled consistently. `.Chart`, by contrast, is per-chart: inside a subchart's own template, `.Chart.Name` and `.Chart.Version` are the *subchart's*, not the parent's. `.Values` is scoped too - a subchart sees its own section of the tree at the top level. So in a 41-service platform chart, `{{ .Release.Name }}` is one shared string across every rendered file while `{{ .Chart.Name }}` differs in each subchart's output, and a name built from both is unique per service without any manual prefixing. ### Read-only, and evaluated once None of these objects can be assigned to. A template can copy a value into a variable, pipe it through functions, or pass a different value as the dot to a partial, but the bound objects themselves do not change during a render. That also means everything derived from them is decided at render time and then frozen into the manifest Helm stores with the release - the rendered text is what gets applied, and re-reading the release later shows the values as they were, not as they would render today.
- Why is it .Chart.AppVersion when Chart.yaml spells the key appVersion?Because `.Chart` is a Go struct, not the raw YAML map, and Go exports only capitalized field names. Helm maps each `Chart.yaml` key onto its struct field, so `appVersion` becomes `AppVersion`, `apiVersion` becomes `APIVersion`. `.Values` behaves differently: it really is a map of whatever keys the values file wrote, so those stay exactly as typed.
- Inside a subchart's own template, what does .Chart refer to, and what does .Release refer to?`.Chart` is the subchart's own `Chart.yaml` - its name and version, not the parent's. `.Release` is the same object the parent sees: one release name, namespace and revision covers the whole install. That asymmetry is why a name composed of `.Release.Name` plus `.Chart.Name` is unique per subchart without any extra prefixing.
- What is .Template.Name useful for?It holds the path of the template file currently rendering, relative to the chart root, and `.Template.BasePath` holds its directory. In practice it is used to stamp a debug annotation onto an object so you can trace a rendered resource back to the file that produced it, and inside `fail` or `required` messages so the error names its own source file.
saying these in an interview costs you the question
- Says .Release.Time gives the install timestamp
- Writes .Chart.appVersion with the Chart.yaml spelling
- Thinks .Values contains only values.yaml defaults
- Believes a template can assign to a built-in object
- Expects .Chart to be the parent chart inside a subchart
- Confuses .Chart.Version with .Chart.AppVersion