Helm Templating
A comprehensive guide to Helm templating covering Go templates, built-in objects, functions, pipelines, and advanced templating patterns for Helm charts.
Go Templates
Built-in Objects
Functions
Pipelines
What is Helm Templating?
Helm uses Go templates to generate Kubernetes manifests dynamically. Templates are files in the templates/ directory that use values, functions, and logic to produce the final YAML output.
Helm templating provides:
- Dynamic Generation: Create manifests based on values and conditions
- Reusability: Use helper functions to avoid duplication
- Flexibility: Customize deployments for different environments
- Logic Control: Use conditionals, loops, and variables
Key Concept: Helm templates are written in Go template syntax with additional functions from Sprig and Helm-specific built-in objects.
Template Syntax
# Basic Template Syntax
# Actions are enclosed in {{ }}
{{ .Values.replicaCount }}
# Comments
{{/* This is a comment */}}
# Variables
{{ $name := .Values.appName }}
{{ $name }}
# Conditionals
{{ if .Values.ingress.enabled }}
# Ingress configuration
{{ else }}
# No ingress
{{ end }}
# Loops
{{ range .Values.extraEnv }}
- name: {{ .name }}
value: {{ .value }}
{{ end }}
# Pipelines (chaining functions)
{{ .Values.image.repository | default "nginx" | quote }}
# Include helper templates
{{ include "my-app.fullname" . }}
# Indentation control
{{- toYaml .Values.resources | nindent 10 }}
# Whitespace control
# - removes whitespace before/after
{{- .Values.key -}}
Actions
Enclosed in
{{ }}. Evaluates expressions and inserts results.Comments
{{/* */}} for template comments. Not included in output.Variables
Use
$ prefix. {{ $var := "value" }} to define.Conditionals
if, else, else if, with, range.Loops
range iterates over arrays, slices, or maps.Pipelines
Chain functions with
|. Output of one is input to the next.
Built-in Objects
.Values
Values passed to the chart from values.yaml and command line. Most commonly used object.
.Release
Information about the release: Name, Namespace, Revision, Service, IsUpgrade, IsInstall.
.Chart
Metadata from Chart.yaml: Name, Version, AppVersion, Description, Type.
.Files
Access files from the chart. Useful for loading configuration files.
.Now
Current time. Useful for timestamps and annotations.
.Capabilities
Information about the Kubernetes cluster: APIVersions, KubeVersion.
# Examples of built-in objects
# .Release
{{ .Release.Name }}
{{ .Release.Namespace }}
{{ .Release.Revision }}
{{ .Release.Service }}
{{ .Release.IsUpgrade }}
# .Chart
{{ .Chart.Name }}
{{ .Chart.Version }}
{{ .Chart.AppVersion }}
{{ .Chart.Description }}
# .Values
{{ .Values.replicaCount }}
{{ .Values.image.repository }}
{{ .Values.resources }}
# .Files
{{ .Files.Get "config/app.conf" }}
{{ .Files.Get "templates/extra.yaml" | indent 2 }}
# .Capabilities
{{ .Capabilities.KubeVersion }}
{{ .Capabilities.APIVersions }}
# .Now
{{ .Now.Format "2006-01-02" }}
Best Practice: Use
.Values for configuration, .Release for release metadata, and .Chart for chart metadata. Avoid hardcoding values directly in templates.
Template Functions
default
Provide a default value:
{{ .Values.key | default "fallback" }}required
Require a value:
{{ required "A valid key is required" .Values.key }}quote
Quote a string:
{{ .Values.key | quote }}toYaml
Convert to YAML:
{{ .Values.resources | toYaml }}toJson
Convert to JSON:
{{ .Values.resources | toJson }}include
Include another template:
{{ include "my-app.fullname" . }}indent
Indent a block:
{{ .Values.resources | toYaml | indent 4 }}nindent
Indent with newline:
{{ .Values.resources | toYaml | nindent 4 }}trunc
Truncate a string:
{{ .Values.name | trunc 63 }}trimSuffix
Trim suffix:
{{ .Values.name | trimSuffix "-" }}contains
Check if string contains substring:
{{ if contains "prod" .Values.environment }}replace
Replace substring:
{{ .Values.name | replace "old" "new" }}# Function examples
# default - fallback value
{{ .Values.image.tag | default "latest" }}
# required - value must exist
{{ required "A valid environment is required" .Values.environment }}
# quote - ensure string quoting
{{ .Values.image.repository | quote }}
# toYaml - format as YAML
{{ .Values.resources | toYaml | indent 4 }}
# include - reusable template
{{ include "my-app.labels" . }}
# nindent - indent with newline
{{- toYaml .Values.annotations | nindent 4 }}
# trunc - limit name length
{{ .Values.name | trunc 63 }}
# trimSuffix - remove trailing dash
{{ .Values.name | trimSuffix "-" }}
# contains - check for substring
{{ if contains "production" .Values.environment }}
# Production-specific logic
{{ end }}
# replace - substitute text
{{ .Values.serviceName | replace "-" "_" }}
Function Tip: Helm includes all functions from the Sprig library (http://masterminds.github.io/sprig/), providing 60+ additional functions for string manipulation, math, date formatting, and more.
Pipelines
Pipelines chain functions together, passing the output of one function as input to the next.
# Basic pipeline
{{ .Values.image.repository | quote }}
# Chain multiple functions
{{ .Values.image.repository | default "nginx" | quote }}
# Complex pipeline
{{ .Values.nameOverride | default .Chart.Name | trunc 63 | trimSuffix "-" }}
# Pipeline with condition
{{ if .Values.fullnameOverride }}
{{ .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{ else }}
{{ .Release.Name | trunc 63 | trimSuffix "-" }}
{{ end }}
# Pipeline with range
{{ range .Values.extraEnv }}
{{ .name | upper | quote }}
{{ end }}
# Pipeline with toYaml and indent
{{- toYaml .Values.resources | nindent 10 }}
# Pipeline with default and required
{{ required "A valid image tag is required" .Values.image.tag | default "latest" }}
# Pipeline with contains and if
{{ if contains "dev" .Values.environment }}
# Development environment
{{ end }}
Pipeline Best Practices:
- Use pipelines for readability and maintainability
- Chain functions logically (data flows from left to right)
- Break complex pipelines into multiple lines
- Use comments to explain pipeline purpose
- Test pipelines with
helm template --debug
Flow Control
# If/Else
{{ if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "my-app.fullname" . }}
annotations:
{{- toYaml .Values.ingress.annotations | nindent 4 }}
spec:
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "my-app.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
# If/Else If/Else
{{ if .Values.ingress.enabled }}
# Ingress enabled
{{ else if .Values.externalService.enabled }}
# External service enabled
{{ else }}
# No external access
{{ end }}
# With (scope)
{{ with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 2 }}
{{- end }}
# Range loop
{{ range $key, $value := .Values.configMap }}
{{ $key }}: {{ $value | quote }}
{{- end }}
# Range with index
{{ range $index, $item := .Values.items }}
- item-{{ $index }}: {{ $item }}
{{- end }}
# Range with break (using variables)
{{ $items := .Values.items }}
{{ range $i, $item := $items }}
{{- if eq $i 5 }}{{ break }}{{ end }}
{{ $item }}
{{- end }}
Flow Control Considerations:
- Use
withfor optional fields (handles nil) - Use
rangefor arrays and maps - Be careful with whitespace (use
{{-and-}}) - Test all branches of conditionals
- Use
$to reference parent scope in loops
Debugging Templates
# Dry-run to see rendered templates
helm install my-release ./my-chart --dry-run --debug
# Render templates only
helm template my-release ./my-chart
# Render with specific values
helm template my-release ./my-chart --set key=value
# Debug specific template
helm template my-release ./my-chart --debug | grep -A 20 "deployment"
# Test template syntax
helm lint ./my-chart
# Render with output to file
helm template my-release ./my-chart > manifests.yaml
# Use --debug for detailed output
helm install my-release ./my-chart --dry-run --debug 2>&1 | less
# Check for template errors
helm template my-release ./my-chart --debug 2>&1 | grep -i error
# Validate generated YAML
helm template my-release ./my-chart | kubectl apply --dry-run=client -f -
Debugging Best Practices:
- Always use
--dry-run --debugbefore installing - Test templates with different values
- Use
helm lintfor syntax checking - Validate generated YAML with
kubectl apply --dry-run - Use
helm templateto see full output
Frequently Asked Questions
What is the difference between template and include?
template renders a template without scope. include renders with scope and can be used in pipelines. Use include for more flexibility and better error handling. How do I access parent scope in a loop?
Use
$ to reference the root context. Example: {{ $.Values.global.key }} inside a range loop. What is whitespace control?
Use
{{- and -}} to trim whitespace. {{- trims leading whitespace, -}} trims trailing whitespace. Essential for clean YAML output. How do I handle missing values?
Use
default for fallback values, required for mandatory values, or with/if for optional blocks. What is the Sprig library?
Sprig is a Go template library that provides 60+ additional functions for string manipulation, math, date formatting, and more. All Sprig functions are available in Helm.
How do I render a template without installing?
Use
helm template <release> <chart> to render templates without installing. This is useful for validation and debugging. What are the most common template functions?
Most common functions:
default, required, quote, toYaml, toJson, include, indent, nindent, trunc, trimSuffix. How do I test templates in isolation?
Create a minimal values file for testing. Use
helm template --set key=value to test specific scenarios. Use --debug to see template evaluation.
Related Topics
Mastering Helm templating is essential for creating flexible and maintainable Helm charts. Practice with different functions and patterns to become proficient.