Go Templating

The Go template engine that powers Helm charts: pipelines, built-in objects, control structures (if/with/range), named templates, tpl function, and whitespace control.

Overview

How Helm renders templates

Helm templates are Go text/template files with two extensions: Sprig (a large library of utility functions) and Helm-specific functions and objects. The engine renders them against the merged values and built-in objects to produce Kubernetes YAML.

Built-in objects

  • .Release — Deployment context: who, where, which revision — .Release.Name, .Release.Namespace, .Release.Revision
  • .Chart — Chart metadata — .Chart.Name, .Chart.Version
  • .Values — Merged user configuration — .Values.image.tag
  • .Capabilities — Cluster capabilities, for feature detection — .Capabilities.APIVersions
  • .Files — Non-template files bundled in the chart — .Files.Get

Pipelines and control structures

Pipelines are the most powerful idiom. {{ .Values.image.tag | default "latest" | quote }} chains default (a fallback) and quote (YAML quotes) left to right.

  • if — Conditionals
  • with — Narrows scope to avoid repeating long paths
  • range — Iterates over lists and maps

Whitespace: the top source of bugs

YAML is indentation-sensitive and Go templates emit whitespace literally. Missing a nindent produces valid template output that is invalid YAML.

> [!IMPORTANT] The {{- -}} hyphen trim syntax and the nindent function are non-negotiable for production charts.

Named templates

Named templates, defined in _helpers.tpl with {{ define }} and called with {{ include }}, are the main code-reuse mechanism. A well-structured chart keeps all naming, labeling and annotation logic there and calls it from every resource template.

> [!TIP] With helpers centralized, a global change (a standard annotation, a new naming scheme) is a one-line edit in _helpers.tpl.

Key concepts

Pipelines
Chain of functions where each function receives the output of the previous as its last argument. {{ .Values.name | upper | trunc 63 | trimSuffix "-" }} — reads a name, uppercases it, truncates to 63 characters (Kubernetes label value limit), removes trailing hyphens. The standard pattern for resource naming.
nindent vs indent
indent "4" adds 4 spaces to each line but no leading newline. nindent "4" adds a leading newline then indents. Use nindent with {{ include }} because the included content needs to start on a new indented line in the YAML output. Missing nindent is the most common whitespace bug.
Whitespace trimming ({{- / -}})
{{- trims all whitespace before the action (left trim). -}} trims all whitespace after the action (right trim). Critical for control structures like {{- if ... }} where the conditional block itself must not introduce blank lines or extra indentation into the rendered YAML.
with scope narrowing
{{ with .Values.resources }} ... {{ end }} sets . to .Values.resources inside the block. Avoids repeating .Values.resources.requests.cpu etc. If the value is empty/nil, the block is skipped entirely — {{ with }} combines nil-check and scope in one construct.
required function
required "message" .Values.field causes helm install to fail immediately with the message if the value is empty or nil. Use for values that have no safe default and must be supplied. Shifts failures from runtime (Pod crashes with missing config) to install time (user gets a clear error).
tpl function
Renders a string value as a Go template. {{ tpl .Values.configData . }} renders a values entry that contains template syntax like {{ .Release.Name }}. Powerful but dangerous: it means user-supplied values can contain arbitrary template code. Use only when explicitly needed for dynamic config generation.

When to use it

  • Writing any resource template that varies by environment or feature flag
  • Creating standard label sets that must be consistent across all resources (define once in _helpers.tpl)
  • Adding conditional resource blocks (Ingress, HPA, PodDisruptionBudget only when enabled)
  • Iterating over user-supplied lists (extra environment variables, extra volume mounts, node selectors)

Interview questions

  • Explain how named templates work in Helm and why {{ include }} is preferred over {{ template }}.
  • How do you handle a nested map value in a template, and what is the toYaml + nindent pattern?
  • How do you implement conditional resources and feature flags in a Helm chart?
  • What are the most common Go templating mistakes and how do you debug them?
  • Explain the Sprig library and which functions are used most in production charts.
  • How do you use the range action to iterate over a list and a map in Helm templates?

Sign up free to read the full topic

More DevOps topics

Start free on Cariara · Coding problems · System design studio