Template Helpers

A comprehensive guide to Helm template helpers covering _helpers.tpl, named templates, include vs template functions, and best practices for reusable Helm templates.

_helpers.tpl Named Templates include vs template
What are Template Helpers?

Template helpers are reusable template functions defined in _helpers.tpl files. They help avoid code duplication and keep templates clean and maintainable.

Helpers are defined using the define action and can be used across multiple templates.

Key Concept: Helpers are the "functions" of Helm templating. They encapsulate common logic like name generation, label creation, and formatting, making templates more DRY (Don't Repeat Yourself).
_helpers.tpl: The Helper File

_helpers.tpl is a special file in the templates/ directory that contains reusable template definitions. It does not produce any output on its own.

# templates/_helpers.tpl {{- define "my-app.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{- define "my-app.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 }} {{- define "my-app.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{- define "my-app.labels" -}} helm.sh/chart: {{ include "my-app.chart" . }} {{ include "my-app.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{- define "my-app.selectorLabels" -}} app.kubernetes.io/name: {{ include "my-app.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{- define "my-app.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "my-app.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} {{- define "my-app.imagePullSecrets" -}} {{- if .Values.image.pullSecrets }} imagePullSecrets: {{- toYaml .Values.image.pullSecrets | nindent 2 }} {{- end }} {{- end }} {{- define "my-app.resources" -}} resources: {{- toYaml .Values.resources | nindent 2 }} {{- end }} {{- define "my-app.environment" -}} {{ .Values.environment | default "production" }} {{- end }}

name

Generates the chart name with overrides. Used for resource naming.

fullname

Generates the full name combining release name and chart name.

labels

Generates standard Kubernetes labels for resource identification.

selectorLabels

Generates selector labels for service-to-pod matching.

serviceAccountName

Generates the service account name with fallback logic.

imagePullSecrets

Generates imagePullSecrets block conditionally.
_helpers.tpl Best Practices:
  • Use define to create reusable functions
  • Prefix helper names with chart name to avoid conflicts
  • Use include to call other helpers
  • Keep helpers focused and single-purpose
  • Document complex helpers with comments
  • Use trunc and trimSuffix for name length limits
Named Templates

Named templates are defined using define and called using include or template.

# Defining a named template {{- define "my-app.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} # Using a named template with include {{ include "my-app.name" . }} # Using a named template with template {{ template "my-app.name" . }} # Named template with parameters {{- define "my-app.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 }} # Named template with multiple lines {{- define "my-app.labels" -}} helm.sh/chart: {{ include "my-app.chart" . }} {{ include "my-app.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} # Nested helpers {{- define "my-app.selectorLabels" -}} app.kubernetes.io/name: {{ include "my-app.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} # Helper with conditional logic {{- define "my-app.ingressPath" -}} {{- if .Values.ingress.path }} {{ .Values.ingress.path }} {{- else }} / {{- end }} {{- end }}
Naming Conventions: Always prefix your helper names with the chart name (e.g., my-app.name) to avoid name collisions when charts are used as subcharts.
include vs template: Key Differences

include

Returns the template output as a string. Can be used in pipelines. Supports chaining with other functions.

template

Renders the template directly. Cannot be used in pipelines. Limited to direct rendering.
# Using include (recommended) {{ include "my-app.labels" . }} # Using include in a pipeline {{ include "my-app.labels" . | indent 4 }} # Using include with nindent {{- include "my-app.labels" . | nindent 4 }} # Using include with toYaml {{- include "my-app.resources" . | toYaml | nindent 2 }} # Using include in a conditional {{ if .Values.ingress.enabled }} {{ include "my-app.ingress" . }} {{ end }} # Using template (limited usage) {{ template "my-app.name" . }} # template cannot be used in pipelines # This will NOT work: {{ template "my-app.name" . | indent 4 }} # This WILL work (using include): {{ include "my-app.name" . | indent 4 }} # include with default {{ include "my-app.fullname" . | default "default-name" }} # include with quote {{ include "my-app.name" . | quote }} # include with trunc {{ include "my-app.fullname" . | trunc 63 }}
Recommendation: Always use include instead of template because:
  • include can be used in pipelines
  • include returns a string (more flexible)
  • include supports chaining with other functions
  • include is the preferred pattern in modern Helm
Common Helper Patterns
# 1. Name generation with fallbacks {{- define "my-app.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} # 2. Full name with release name {{- define "my-app.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. Labels with consistent structure {{- define "my-app.labels" -}} helm.sh/chart: {{ include "my-app.chart" . }} {{ include "my-app.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} # 4. Selector labels for services {{- define "my-app.selectorLabels" -}} app.kubernetes.io/name: {{ include "my-app.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} # 5. Conditional blocks {{- define "my-app.ingress" -}} {{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "my-app.fullname" . }} labels: {{- include "my-app.labels" . | nindent 4 }} 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 }} {{- end }} # 6. Resource formatting {{- define "my-app.resources" -}} {{- if .Values.resources }} resources: {{- toYaml .Values.resources | nindent 2 }} {{- end }} {{- end }} # 7. Environment variable helpers {{- define "my-app.env" -}} {{- range $key, $value := .Values.extraEnv }} - name: {{ $key | quote }} value: {{ $value | quote }} {{- end }} {{- end }}
Helper Pattern Best Practices:
  • Use default for fallback values
  • Use trunc and trimSuffix for name length limits
  • Use include to compose helpers
  • Keep helpers focused and single-purpose
  • Use with for optional blocks
  • Use range for loops
Debugging Helpers
# Debug helper to inspect values {{- define "my-app.debug" -}} {{- printf "Values: %v" .Values | nindent 0 }} {{- end }} # Use debug helper in template {{ include "my-app.debug" . }} # Helper to check if a value exists {{- define "my-app.hasValue" -}} {{- if .Values.key }} {{- true }} {{- else }} {{- false }} {{- end }} {{- end }} # Helper to print variable {{- define "my-app.printVar" -}} {{- printf "Variable: %s" . | nindent 0 }} {{- end }} # Dry-run to see rendered helpers helm template my-release ./my-chart --debug | grep -A 5 "my-app" # Check helper syntax helm lint ./my-chart # Render specific helper helm template my-release ./my-chart --debug | grep -A 10 "my-app.name"
Frequently Asked Questions
What is _helpers.tpl?
_helpers.tpl is a special file in the templates/ directory that contains reusable template definitions. It does not produce any output on its own.
What is the difference between include and template?
include returns the template output as a string and can be used in pipelines. template renders directly and cannot be used in pipelines. Always use include.
Why should I use helpers?
Helpers avoid code duplication, make templates more maintainable, and encapsulate common logic like name generation and label creation.
How do I name helpers?
Prefix helper names with the chart name (e.g., my-app.name) to avoid name collisions when charts are used as subcharts.
Can helpers call other helpers?
Yes! Helpers can call other helpers using include. This enables composition and code reuse.
Where should I put helper functions?
Put helper functions in templates/_helpers.tpl. This is the standard location for reusable template definitions.
Can I use helpers from subcharts?
Helpers are scoped to their chart. You cannot directly use helpers from parent charts in subcharts. However, you can use include with the chart name prefix.
How do I debug helper functions?
Use helm template --debug to see rendered output. Add temporary printf statements in helpers for debugging. Use helm lint for syntax checking.
Previous: Values Management Next: Sprig Functions

Template helpers are essential for creating maintainable Helm charts. Use _helpers.tpl to define reusable functions and always prefer include over template for flexibility.