skip to content

Built-in Objects

The objects Helm binds before a template runs — .Values, .Release, .Chart, .Template, .Files and .Subcharts. Asked to see which of them are safe to build a resource name from.

part ofHelmoverview, primer and where to startread it →
on this pageshow

questions

4

In a Helm chart, which built-in objects are bound before the templates render?

level: juniorimportance: must knowfreq 76%

answer

  1. Seven names exist before any function runs
  2. One holds user input, one holds the release
  3. Capitalized because they are Go struct fields
  4. Release is shared, Chart is per-chart
  5. Values, Release, Chart, Files, Template, Subcharts

basics

~10 s

Helm 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 s

Before 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 lines
yaml
metadata:
  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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context

open as a page

In a Helm chart, what do .Release.IsInstall, .Release.IsUpgrade and .Release.Revision report?

level: middleimportance: should knowfreq 57%

basics

~20 s

They describe the operation currently rendering. On install, IsInstall is true, IsUpgrade is false and Revision is 1. On upgrade the first two swap and Revision is the number of the revision being created, one higher than the current one.

open as a page

Which Helm built-in objects are safe to build a Kubernetes resource name from?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Only the ones that do not change between upgrades: .Release.Name, .Chart.Name and stable values. .Chart.Version, .Chart.AppVersion, .Release.Revision and .Release.IsInstall all move, and a moved name makes Helm create a new object and delete the old one.

open as a page

What can a Helm chart read with .Files.Get and .Files.Glob, and what is invisible to them?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

.Files reads non-template files packaged inside the chart, by path relative to the chart root. Get returns one file's contents as a string, Glob returns a set. Files under templates/ and anything .helmignore excluded are invisible, and a missing path returns an empty string rather than an error.

open as a page