Skip to main content

Week 1 of the Helm Template Guide: the 3 concepts that finally clicked

· 3 min read
Charles Wang
DevOps / Backend Engineer

I started picking up Helm this week — reading the official template guide while writing a chart by hand, and also taking apart one of Bitnami's big charts. Here are the three concepts I found most important, written down as notes to my future self.

1. helm template is not the same as helm install --dry-run

helm template only does local rendering: it reads Chart.yaml and templates/, substitutes the values from values.yaml into the templates, and prints plain YAML — it never touches the cluster.

helm install --dry-run, on top of rendering, sends the result to the kube-apiserver for schema validation and adds Helm's own metadata. So dry-run can catch problems that template can't see (for example, a field with the wrong type). This is also why ArgoCD uses helm template and then applies the output itself, rather than helm install — it wants predictable plain YAML and doesn't want Helm taking over cluster state.

When paired with ArgoCD, ArgoCD prefers helm template over helm install, because it wants to be the single declarative state manager. It treats Helm as nothing more than a rendering tool, instead of handing control of cluster state over to Helm's release mechanism.

2. Values with a default / quote pipeline

Reading a value directly with {{ .Values.image.repository }} renders to a blank when the value is missing — an easy trap. A pipeline makes it much safer:

replicas: {{ .Values.replicaCount | default 1 }}
image: {{ .Values.image.repository | default "nginx" | quote }}

A pipeline is evaluated left to right, passing the result down one stage at a time: default "nginx" supplies a fallback when the value is missing, and quote then turns the result into a quoted string. When a value is missing, default saves you from rendering an empty string that would break the whole YAML.

3. Helpers and the two dashes {{- -}}

_helpers.tpl is where you put reusable snippets (DRY). Helpers are almost always written as {{- define "x" -}} rather than {{ define "x" }}; those leading and trailing dashes (-) trim the surrounding whitespace, including newlines, so the string produced by include stays clean and doesn't wreck your YAML indentation.

By contrast, the _helpers.tpl that helm create generates is very simple, because it only deals with basic objects like deployment / service / ingress. Bitnami's helpers are far more complex because it uses many labels and has many values that need to be referenced repeatedly.

Side note: the MDX traps I hit

Writing this post in Docusaurus, I ran into two traps:

  • HTML comments break the build. <!-- --> is invalid in MDX; you have to use the MDX comment {/* */} instead.
  • Bare double braces get treated as JSX. A {{ .Values.xxx }} in the prose, if left unwrapped, makes MDX try to evaluate it as a JavaScript expression and error out — so wrap it in a code fence or inline code (backticks).

Wrap-up

Getting these three things straight in week one — template vs install, the values pipeline, and helpers with their dashes — is essentially getting familiar with the skeleton of Helm's rendering flow. Next week I'll keep building on top: dependency subcharts, hooks, and then actually installing the chart into a cluster to run a round of install / upgrade / rollback.