- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. Go Template Internals
- 2. Sprig Library Functions
- 3. Named Templates
- 4. Flow Control
- 5. Advanced Template Patterns
- 6. Library Charts
- 7. Debugging
- 8. Summary
1. Go Template Internals
1.1 Parsing Stage
Helm's template engine is built on Go's text/template standard library. Template processing is divided into parsing and execution stages.
The parsing stage transforms template text into an AST (Abstract Syntax Tree):
Template text -> Lexer -> Token stream -> Parser -> AST
1.2 Execution Stage
The execution stage traverses the AST while applying the data context (dot .):
AST + Data context -> Rendered text output
The Pipeline concept is key:
# Pipeline: left output is passed as right input
name: { { .Values.name | lower | trunc 63 | trimSuffix "-" } }
# Execution order:
# 1. Evaluate .Values.name -> "My-Application-Name"
# 2. Apply lower -> "my-application-name"
# 3. Apply trunc 63 -> "my-application-name"
# 4. Apply trimSuffix "-" -> "my-application-name"
1.3 Whitespace Control
# Hyphens (-) remove whitespace
metadata:
labels:
{{- include "my-chart.labels" . | nindent 4 }}
#^ removes left whitespace/newlines
{{ include "my-chart.labels" . | nindent 4 -}}
# ^ removes right whitespace/newlines
{{- include "my-chart.labels" . | nindent 4 -}}
#^ removes both sides
2. Sprig Library Functions
2.1 String Functions
upper: {{ "hello" | upper }} # HELLO
lower: {{ "HELLO" | lower }} # hello
title: {{ "hello world" | title }} # Hello World
trim: {{ " hello " | trim }} # hello
substr: {{ substr 0 5 "hello world" }}# hello
trunc: {{ "hello" | trunc 3 }} # hel
contains: {{ contains "lo" "hello" }} # true
replace: {{ "hello world" | replace " " "-" }} # hello-world
snakecase: {{ "HelloWorld" | snakecase }} # hello_world
camelcase: {{ "hello_world" | camelcase }} # HelloWorld
kebabcase: {{ "HelloWorld" | kebabcase }} # hello-world
regexMatch: {{ regexMatch "^[a-z]+$" "hello" }} # true
randAlphaNum: {{ randAlphaNum 10 }} # 10-char random alphanumeric
2.2 Math Functions
add: { { add 1 2 } } # 3
sub: { { sub 10 3 } } # 7
mul: { { mul 2 3 } } # 6
div: { { div 10 3 } } # 3
mod: { { mod 10 3 } } # 1
max: { { max 1 2 3 } } # 3
min: { { min 1 2 3 } } # 1
ceil: { { ceil 1.1 } } # 2
floor: { { floor 1.9 } } # 1
round: { { round 1.5 0 } } # 2
2.3 Date Functions
now: { { now } }
date: { { now | date "2006-01-02" } }
dateModify: { { now | dateModify "+24h" | date "2006-01-02" } }
2.4 Crypto Functions
sha256sum: {{ "hello" | sha256sum }}
b64enc: {{ "hello" | b64enc }} # aGVsbG8=
b64dec: {{ "aGVsbG8=" | b64dec }} # hello
uuid: {{ uuidv4 }}
2.5 List Functions
list: { { list "a" "b" "c" } }
first: { { first (list "a" "b" "c") } } # a
last: { { last (list "a" "b" "c") } } # c
append: { { append (list "a" "b") "c" } } # [a b c]
has: { { has "b" (list "a" "b" "c") } } # true
without: { { without (list "a" "b" "c") "b" } } # [a c]
uniq: { { uniq (list "a" "b" "a") } } # [a b]
sortAlpha: { { sortAlpha (list "c" "a" "b") } } # [a b c]
2.6 Dictionary Functions
{{- $myDict := dict "key1" "value1" "key2" "value2" }}
get: {{ get $myDict "key1" }} # value1
hasKey: {{ hasKey $myDict "key1" }} # true
keys: {{ keys $myDict }} # [key1 key2]
values: {{ values $myDict }} # [value1 value2]
{{- $merged := merge (dict "a" "1") (dict "b" "2") }}
3. Named Templates
3.1 define and template
# templates/_helpers.tpl
{{- define "my-chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "my-chart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
3.2 template vs include
# template: outputs directly (no pipeline)
{{ template "my-chart.name" . }}
# include: returns as string (pipeline available)
{{ include "my-chart.labels" . | nindent 4 }}
include is recommended because it supports post-processing via pipelines (indentation, etc.).
3.3 _helpers.tpl Convention
# Standard helper patterns
{{- define "my-chart.labels" -}}
helm.sh/chart: {{ include "my-chart.chart" . }}
{{ include "my-chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "my-chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
4. Flow Control
4.1 if/else
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "my-chart.fullname" . }}
{{- end }}
{{- if eq .Values.service.type "NodePort" }}
nodePort: {{ .Values.service.nodePort }}
{{- else if eq .Values.service.type "LoadBalancer" }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
Falsy values: false, 0, empty string "", nil, empty collections
4.2 with
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
# Access parent scope with $ inside with blocks
{{- with .Values.container }}
name: {{ $.Chart.Name }}
image: {{ .image }}
{{- end }}
4.3 range
# List iteration
{{- range .Values.extraEnvVars }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
# Map iteration
{{- range $key, $value := .Values.configData }}
{{ $key }}: {{ $value | quote }}
{{- end }}
# Fixed count
{{- range until 5 }}
- replica-{{ . }}
{{- end }}
5. Advanced Template Patterns
5.1 toYaml and nindent
spec:
template:
spec:
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
5.2 tpl Function
# values.yaml
configTemplate: |
server.name={{ .Release.Name }}
# templates/configmap.yaml
data:
config.properties: |
{{ tpl .Values.configTemplate . | nindent 4 }}
5.3 lookup Function
{{- $secret := lookup "v1" "Secret" .Release.Namespace "my-secret" }}
{{- if $secret }}
password: {{ index $secret.data "password" }}
{{- else }}
password: {{ randAlphaNum 16 | b64enc }}
{{- end }}
Note: lookup always returns empty results with helm template.
6. Library Charts
Library charts (type: library) render no resources themselves, only provide named templates:
# Chart.yaml
apiVersion: v2
name: my-library
type: library
version: 1.0.0
Benefits: shared template logic, centralized label/annotation standards, DRY compliance.
7. Debugging
# Local template rendering
helm template my-release ./my-chart
helm template my-release ./my-chart -s templates/deployment.yaml
helm template my-release ./my-chart --debug
# Lint
helm lint ./my-chart --strict
8. Summary
The Helm template engine provides a powerful programming model beyond simple string substitution:
- Go template-based: Two-stage parsing-execution pipeline architecture
- Sprig library: Over 100 utility functions
- Named templates: Modular, reusable templates with define/include
- Flow control: Conditional and iterative rendering with if/with/range
- Library charts: Centralized management and sharing of common logic
The next post analyzes the complete Helm release lifecycle (install, upgrade, rollback).