skip to content

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

level: middleimportance: nice to knowfreq 32%

answer

  1. Reads what is packaged, not what is nearby
  2. Paths are relative to the chart root
  3. Two directories' worth of content is invisible
  4. A missing path is silent, not an error
  5. Glob has AsConfig and AsSecrets

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.

solid answer

~40 s

`.Files` is the built-in object that exposes the chart's own non-template files - the ones packaged into the `.tgz` alongside `templates/`. `.Files.Get "config/ledger.toml"` returns that file's contents as a string; `.Files.GetBytes` returns raw bytes for binary content, which you normally pipe through `b64enc`; `.Files.Lines` returns a slice you can `range` over. `.Files.Glob "config/*.toml"` returns a set of matching files, and that set has `AsConfig` to emit base-name-keyed entries for a ConfigMap's `data` and `AsSecrets` to emit the same base64-encoded for a Secret. Three things are invisible: anything under `templates/`, anything `.helmignore` excluded from the package, and anything outside the chart directory. A path that matches nothing yields an empty string with no error, so pair it with `required` when the file is mandatory.

code

yaml · 8 lines
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-ledger-config
data:
{{ (.Files.Glob "config/*.toml").AsConfig | indent 2 }}
  ledger.toml: |-
{{ .Files.Get "config/ledger.toml" | required "config/ledger.toml is missing from the chart" | nindent 4 }}

go deeper

for a junior

Know that .Files.Get takes a path relative to the chart root and returns the file's contents as a string, and that the file has to be part of the chart - it is not reading your laptop.

for a middle

Explain the whole small API - Get, GetBytes, Lines, Glob with AsConfig and AsSecrets - and the three invisibility rules: templates/, .helmignore, and outside the chart. Mention that a missing file is silently empty.

for a senior

Show the operational angles: the directory-versus-package divergence a .helmignore pattern causes, guarding mandatory files with required, and why vendoring large assets into a chart eventually collides with the size of the record Helm stores the release in.

for a principal

Decide the policy: what belongs in the chart as a file versus what must be an overridable value, since anything read through .Files is fixed for every consumer and can only be changed by forking or republishing the chart.

### What .Files is A chart is a directory, and not everything in it is a template. Alongside `Chart.yaml`, `values.yaml` and `templates/`, a chart can carry ordinary files - a configuration file, a dashboard definition, a SQL migration, a certificate bundle. `.Files` is the built-in object that lets a template read those files and embed their contents in rendered output. Every path it takes is relative to the chart root, and everything it can see is packaged into the chart archive when the chart is built. ### The API `.Files.Get "path"` returns the file's contents as a string. `.Files.GetBytes "path"` returns raw bytes, which is what you want for anything not UTF-8 text - typically piped into `b64enc` on the way into a Secret. `.Files.Lines "path"` returns the file split into a slice, so you can `range` over it and transform line by line. `.Files.Glob "pattern"` takes a shell-style pattern and returns the set of files matching it. That set is more than a list: it has `AsConfig`, which emits `key: value` YAML with each file's *base name* as the key and its contents as the value - the exact shape a ConfigMap's `data` block wants - and `AsSecrets`, which emits the same thing with the values base64-encoded for a Secret's `data`. It also supports `Get` and `Lines` against the matched subset. Because the output is being spliced into YAML, indentation is on you: a file's contents dropped straight into a `data` block will not line up unless it is piped through `nindent`, and a multi-line file needs a block scalar such as `|-` on the key. ### What it cannot see Three exclusions matter, and interviewers ask about all three: **Files under `templates/`.** Those are templates, and `.Files` will not hand you their source. A chart cannot read its own template files as data through this object. **Anything `.helmignore` excluded.** `.helmignore` decides what goes into the package. A file that is ignored is not in the archive, so it is not in `.Files` either. This is the failure that bites in CI and not on a developer's laptop: rendering from a directory may find a file that rendering from the packaged chart will not, if that file matched an ignore pattern. **Anything outside the chart directory.** There is no way to reach up out of the chart into the filesystem of whoever is running Helm. That is deliberate - a chart is meant to be a self-contained, distributable artifact - and it is why there is no "read this file from the operator's machine" feature. ### The silent-empty-string trap `.Files.Get` on a path that does not exist does not fail. It returns an empty string, and the render continues. The result is usually a ConfigMap with an empty key, or a config file mounted into a container with no content in it - and the failure only surfaces when the workload starts and cannot parse its configuration. A chart that genuinely requires a file should say so: data: ledger.toml: |- {{ .Files.Get "config/ledger.toml" | required "config/ledger.toml is missing from the chart" | nindent 4 }} `required` fails the render with your message when the value is empty, which turns a mysterious runtime failure into an obvious install-time one. ### Scoping and size `.Files` is scoped to the chart being rendered. A dependency's templates see the dependency's own files; the parent's templates see the parent's. That follows the same pattern as `.Chart`, which is also per-chart, and is different from `.Release`, which is shared across the whole install. Size deserves a word. Files reachable through `.Files` travel inside the chart archive, and Helm stores the release - chart included - compressed in the release record it keeps in the namespace. That record is a Secret, and Secrets have an upper size limit. A chart that vendors large binaries or a big pile of dashboards can grow until installs start failing on the size of the object Helm is trying to write, which is a confusing error to diagnose from the message alone. Keep `.Files` for configuration-sized content; genuinely large assets belong in an image or fetched at runtime. ### When to reach for it The honest answer is: less often than people expect. If the content varies per environment it belongs in values, where callers can override it. `.Files` is for content that is genuinely part of the chart and identical for every installation - a default configuration file that is easier to maintain as a real file than as a YAML string, a set of dashboards a library chart consumed by twelve service charts ships for all of them, a licence or schema file that a tool elsewhere also reads. Keeping it as a real file means editors, linters and tests for that format still work on it, which is the whole argument for the object.

  • A chart renders fine from a directory but the packaged chart produces an empty ConfigMap key. What is the likely cause?
    A `.helmignore` pattern excluded the file from the package. `.Files` can only see what was packaged, so rendering the source directory finds it and rendering the `.tgz` does not. Because `.Files.Get` returns an empty string for a path it cannot find rather than failing, the difference is silent until the workload starts. Check `.helmignore`, and guard mandatory files with `required`.
  • What does .Files.Glob's AsConfig produce, and how does AsSecrets differ?
    `AsConfig` emits YAML mapping each matched file's base name to its contents - exactly the shape a ConfigMap's `data` block expects. `AsSecrets` emits the same mapping with each value base64-encoded, for a Secret's `data`. Both still need indenting into place with `indent` or `nindent`, since you are splicing generated YAML into a document.
  • Can a parent chart read a dependency's files through .Files?
    No. `.Files` is scoped to the chart being rendered, the same way `.Chart` is - a dependency's templates see the dependency's own files and the parent's see the parent's. `.Subcharts` gives a parent read access to a dependency's chart metadata and values, not to its packaged files. If content must be shared, it belongs in values or in a chart both depend on.
  • Why not put a whole config file into .Files instead of exposing values?
    Because `.Files` content is fixed for every installation - a caller cannot override it without forking the chart. Use it for content that genuinely never varies, where keeping it as a real file lets editors, linters and format-specific tests work on it. Anything that differs per environment belongs in values, which is the surface consumers are allowed to change.

saying these in an interview costs you the question

  • Expects .Files.Get to error on a missing path
  • Thinks .Files can read files under templates/
  • Believes .Files reaches files outside the chart directory
  • Forgets that .helmignore removes files from the package
  • Splices file contents in without indent or nindent
  • Uses .Files for content that should be overridable values

context