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 with for optional fields (handles nil)
  • Use range for 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 --debug before installing
  • Test templates with different values
  • Use helm lint for syntax checking
  • Validate generated YAML with kubectl apply --dry-run
  • Use helm template to 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.
Previous: Chart Hooks Next: Values Management

Mastering Helm templating is essential for creating flexible and maintainable Helm charts. Practice with different functions and patterns to become proficient.