skip to content

How do you render a Helm chart to YAML locally, and what does helm template --show-only do?

level: juniorimportance: must knowfreq 78%

answer

  1. Render without touching the cluster
  2. The command that prints YAML to stdout
  3. Narrowing the stream to one template file
  4. -s / --show-only takes a template path
  5. --output-dir writes one file per template

basics

~20 s

helm template <release-name> <chart> renders the chart with the values you pass and prints the resulting YAML to stdout — no cluster call, no release record. Adding -s/--show-only templates/job.yaml limits the output to that one template file's manifests.

solid answer

~50 s

`helm template` runs Helm's rendering engine and stops. It substitutes `.Values`, `.Release` and `.Chart` into the files under `templates/`, then writes the results to stdout as one YAML stream, each document separated by `---` and labelled with a `# Source: <chart>/templates/<file>` comment. Nothing is applied and no release revision is stored, so it is the safe first move when a chart behaves unexpectedly. Values come from the same flags as install — `-f`, `--set`, `--set-string`, `--set-file` — so you can reproduce exactly what a pipeline would send. On a large chart the output runs to thousands of lines, so `-s`/`--show-only templates/indexer-job.yaml` narrows it to the manifests one template file produced; repeat the flag for several files, and note it filters by template path, not by Kubernetes kind. `--output-dir DIR` is the other narrowing tool: one file per template instead of one stream.

code

bash · 9 lines
bash
# render the whole chart the way the pipeline would
helm template indexer ./operator-chart -f values-prod.yaml > rendered.yaml

# only the one manifest under discussion
helm template indexer ./operator-chart -f values-prod.yaml \
  -s templates/indexer-job.yaml

# one file per template instead of one stream, for diffing
helm template indexer ./operator-chart -f values-prod.yaml --output-dir ./out

go deeper

for a junior

Know the command and that it prints YAML instead of changing anything. Be ready to say which flags supply values and how you would look at just one template's output rather than scrolling through the whole chart.

for a middle

Explain the mechanics: which built-in objects are bound during the render, why --show-only matches template paths rather than resource kinds, and how # Source: comments map a line of output back to the file that produced it.

for a senior

Show the workflow habit — reproducing a pipeline's exact flags locally to separate a chart bug from a values bug, and using --output-dir so two renders diff per template instead of as one enormous hunk.

for a principal

Own the policy question: which stages of delivery get a render-only check with no cluster credentials at all, whether golden renders are reviewed in pull requests, and what that costs in churn versus what it catches.

### Two halves of Helm, and only one of them runs here Helm does two separate jobs. The first is **rendering**: walk the chart directory, execute every file under `templates/` as a Go template with `.Values`, `.Release`, `.Chart` and the other built-in objects bound, and produce a stream of Kubernetes YAML. The second is **release management**: send that YAML to the API server, wait if asked, and store a record of what was applied so the release can be listed, upgraded and rolled back later. `helm template` runs the first job and stops. It is a local, offline transformation from *chart + values* to *text*. Nothing is applied, no release record is written, and by default no request is made to a cluster at all. That is precisely why it is the first command to reach for when a chart does something surprising: it isolates "did the chart produce the YAML I expected?" from "did the cluster accept it?", which are two different bugs with two different fixes. ### The shape of the output The command is `helm template [NAME] [CHART] [flags]`. `CHART` is the same argument `helm install` takes — a local directory, a packaged `.tgz`, a `repo/name` reference or an `oci://` reference. `NAME` is the release name that `.Release.Name` will resolve to during the render; if you leave it out, Helm substitutes a placeholder, and every resource name derived from it in the output will be the placeholder rather than what a real install would create. Pass the real name whenever you intend to compare the output with something. What comes back on stdout is a single YAML stream: each rendered file separated by `---` and prefixed with a `# Source: <chart>/templates/<file>` comment. Those `# Source:` lines are the map from output back to input — when a stray field appears three hundred lines down, scrolling up to the nearest `# Source:` tells you which template to open. The chart's `NOTES.txt` is not part of that manifest stream; it is post-install human text, not a manifest. ### Values come from the same place they would on install `-f/--values`, `--set`, `--set-string`, `--set-file`, `--set-json` and `--set-literal` all behave exactly as they do on `helm install`, and they compose in the same order. This is the property that makes `helm template` a debugging tool rather than a toy: you can copy the flags out of a pipeline definition verbatim, run them locally, and be looking at the same bytes the pipeline would have produced. If the local render is correct and the deployed object is not, the difference is in the values the pipeline actually passed, not in the chart. ### Narrowing a large render Real charts render thousands of lines, and a chart that ships a whole operator plus its workloads renders far more. Two flags cut that down. `-s`, long form `--show-only`, takes a **template path relative to the chart root** — for example `templates/indexer-job.yaml` — and limits the output to the manifests that one file produced. It is a filter over *sources*, not over Kubernetes kinds or resource names: passing `Job` or the rendered object's `metadata.name` does not work. Repeat the flag to keep several files. If nothing in the chart matches the path you gave, Helm reports an error rather than quietly printing an empty document, which is a small mercy — a typo in the path fails loudly instead of looking like "this template renders nothing". `--output-dir DIR` takes the other approach: instead of one stream on stdout, write each rendered template to its own file under `DIR`, mirroring the chart's structure. That is the form you want when you plan to diff two renders, because a per-file diff attributes each change to a template instead of showing one enormous hunk. ### What it deliberately does not do `helm template` does not check that the release name is free, does not consult the target cluster's real API versions, does not run admission webhooks, and does not validate field names or types against any schema. A manifest that renders perfectly can still be rejected the moment it is sent to a real API server. Preview commands that involve the cluster exist for exactly that gap, and choosing between them is a separate decision. The other habit worth building: because the output is just text, it composes. Pipe it into a pager while hunting, into a file to commit as a golden render, or into a diff against yesterday's render to see exactly what a values change did — all without a cluster and without a release.

  • Does the release name you pass to `helm template` matter?
    Yes. It is what `.Release.Name` resolves to during the render, and most charts build resource names and labels from it. Omit it and Helm substitutes a placeholder, so every derived name in the output differs from what a real install would create. Pass the real name whenever you intend to compare the render against something.
  • Is `helm template | kubectl apply -f -` a reasonable way to install a chart?
    It works, and some teams do it deliberately to keep cluster writes in one tool. What you give up is everything the release manager provides: no release record, so no `helm list`, `helm history`, `helm rollback` or `helm uninstall`; hooks and their ordering are not executed; and nothing tracks resources that a later version of the chart removes. Choose it knowingly, not by accident.

saying these in an interview costs you the question

  • Says helm template applies the manifests to the cluster
  • Thinks helm template creates or updates a release revision
  • Passes --show-only a Kubernetes kind or a resource name
  • Assumes the render equals what is running in the cluster now
  • Believes helm template needs a cluster connection by default

context