Templates are the heart of Helm. Under the hood Helm uses Go’s template engine plus a large library of helper functions. Don’t worry if you’ve never seen Go — the syntax you need is small and we’ll build it up piece by piece.
Template actions
Anything between double curly braces is a template action that Helm evaluates:
metadata:
name: {{ .Release.Name }}
Everything else is copied through verbatim. So a template is just your normal YAML with small dynamic holes punched in it.
Reading values
The most common action is reading a value from values.yaml through the .Values
object. Given:
# values.yaml
replicaCount: 2
image:
repository: nginx
tag: "1.27"
you reference nested keys with dots:
spec:
replicas: {{ .Values.replicaCount }}
containers:
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
Built-in objects
Besides .Values, Helm gives you several built-in objects. The ones you’ll use most:
.Release.Name— the release name (e.g.my-redis)..Release.Namespace— the target namespace..Release.Service— alwaysHelm..Chart.Name,.Chart.Version,.Chart.AppVersion— fields fromChart.yaml..Values— your merged values..Files— access non-template files bundled in the chart..Capabilities— what the target cluster supports (API versions, Kube version).
For example, a fully-qualified resource name often combines the release name and chart name:
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
Pipelines
Helm borrows the Unix pipe idea: send a value through one or more functions with
|. The value on the left becomes the last argument to the function on the right.
# uppercase the release name
name: {{ .Release.Name | upper }}
# provide a fallback if the value is empty
tag: {{ .Values.image.tag | default .Chart.AppVersion }}
# quote a string so YAML treats it as text
version: {{ .Chart.AppVersion | quote }}
Pipelines chain neatly:
name: {{ .Values.name | default "web" | lower | trunc 63 }}
Handy functions
Helm includes the Sprig function library. A few you’ll use constantly:
default DEFAULT VALUE— fall back when a value is missing.quote/squote— wrap in double / single quotes.upper/lower/title— change case.trunc NandtrimSuffix— trim strings (useful to respect the 63-char name limit).nindent N— indent a block by N spaces, starting with a newline (essential for embedding blocks of YAML).toYaml— turn a values structure back into YAML.
toYaml with nindent is the classic combo for passing through a whole block, like
resource limits:
resources:
{{- toYaml .Values.resources | nindent 2 }}
Whitespace control
YAML is whitespace-sensitive, so Helm gives you dash modifiers to trim it. A {{-
chomps whitespace (including the newline) before the action; -}}
chomps after it.
metadata:
labels:
{{- if .Values.extraLabel }}
team: platform
{{- end }}
Without the leading dashes you’d get blank lines where the if/end used to be. When
your rendered YAML has odd indentation or empty lines, whitespace control is almost
always the fix — and helm template shows you the exact result.
Comments
Template comments never reach the output:
{{/* This explains the next block and is stripped at render time. */}}
Putting it together
Here’s a small but realistic container spec using several of these ideas:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy | default "IfNotPresent" }}
ports:
- containerPort: {{ .Values.service.port }}
The example webapp chart for this chapter is in
03-templating-basics.
We can now read values and shape them with functions. In the next chapter we’ll add logic — conditionals, loops, and reusable named templates — which is where charts get truly powerful.