Introduction to Named Templates in Helm
In Helm, Named Templates are a powerful tool that allows you to define reusable pieces of code. These code blocks can be invoked multiple times throughout a Helm chart, reducing redundancy, and enhancing maintainability.
The concept of Named Templates introduces the principle of Don’t Repeat Yourself (DRY) into Helm charts. By defining a common piece of code once and then reusing it wherever required, you create a more efficient, easier-to-maintain structure.
Named Templates are typically defined in a special file named
_helpers.tpl that resides within the templates/ directory of a Helm
chart. However, they can technically be defined in any file within the
templates/ directory.
The definition of a Named Template looks like this:
{{- define "template-name" -}}
# Your template content here
{{- end -}}
Here, "template-name" is the name of the template. This is how you
will reference the template elsewhere in your Helm chart. The content of
the template is placed between the {{- define "template-name" -}} and
{{- end -}} tags.
For example, if you have a common set of labels that are used in multiple Kubernetes objects within your Helm chart, you might define a Named Template for them like so:
{{- define "mychart.labels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
helm.sh/chart: {{ include "mychart.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
In this example, mychart.labels is the name of the template, and it
defines four labels that can be reused in other templates in the Helm
chart.
The scope of the variables within the Named Template is limited to the
template itself. However, you can pass in variables from the parent
scope using the . (dot) notation, as shown above. The . represents
the current scope, which contains all of the top-level objects like
.Release and .Values.
Creating Named Templates
Creating a Named Template in Helm involves several steps. Here’s a step-by-step guide:
Step 1: Create a Helm Chart
If you don’t already have a Helm chart, create one using the
helm create command. This will generate a skeleton chart with all the
necessary files.
helm create mychart
Step 2: Navigate to the Templates Directory
Go to the templates/ directory that has been created in your Helm
chart.
cd mychart/templates/
Step 3: Open the _helpers.tpl file
The _helpers.tpl file is where you’ll typically store Named Templates.
Open this file in your preferred text editor.
Step 4: Define a Named Template
At the end of the _helpers.tpl file, define a new Named Template using
the define action. For instance:
{{- define "mychart.standardLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
helm.sh/chart: {{ include "mychart.chart" . }}
{{- end -}}
Here, “mychart.standardLabels” is the name of the Named Template.
Step 5: Save the _helpers.tpl file
Save the changes you’ve made to the _helpers.tpl file.
Step 6: Use the Named Template in Your Chart
You can now use the Named Template in any other template within your
Helm chart. To use the Named Template, include it with the include
function, like this:
apiVersion: v1
kind: Service
metadata:
name: {{ include "mychart.name" . }}
labels:
{{- include "mychart.standardLabels" . | nindent 4 }}
spec:
...
Step 7: Verify Your Templates
Use the helm lint command to check your chart for possible issues:
helm lint ../
Step 8: Test Your Chart
Use the helm install --dry-run --debug command to test your chart
without actually deploying it:
helm install --dry-run --debug myrelease ../
Replace “myrelease” with the release name of your choice.
Using Named Templates
Once you’ve defined a Named Template, you can use it in any other
template within your Helm chart by using the include function. This
function allows you to inject the output of the Named Template into your
Kubernetes manifest files.
Here’s how you can use a Named Template in your Helm charts:
- Identify the Template Name: Before you can use a Named Template, you need to know its name. In the case of our previous example, the Named Template’s name was “mychart.standardLabels”.
- Call the Template with the Include Function: Use the
includefunction to call the Named Template from another template. You’ll pass two arguments to theincludefunction: the name of the Named Template, and the current scope (represented by.). The scope contains all of the top-level objects available in your Helm chart, such as.Chart,.Release, and.Values.
For example, if you want to use the “mychart.standardLabels” Named Template in a Service object, you would do it like this:
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-service
labels:
{{- include "mychart.standardLabels" . | nindent 4 }}
spec:
...
In this example, the include function injects the output of the
“mychart.standardLabels” Named Template into the labels field of the
Service object. The nindent 4 function ensures the output lines up
correctly with the YAML format by adding a new line and four spaces of
indentation before the output.
Passing Values to Named Templates
1. Using include function
Named Templates in Helm can accept values passed to them when they’re
called using the include function. The values are passed in the form
of the scope object. The scope object in Helm can contain a variety of
top-level objects such as .Chart, .Release, .Values, and so on.
When you call a Named Template with the include function, you pass it
a scope object. This object is often represented by the . (dot)
character. Here’s what it looks like in practice:
{{- include "mychart.standardLabels" . }}
In this case, . is the scope object, and it represents the current
context or scope, which includes all of the top-level objects in your
chart. You can think of it like the this keyword in many programming
languages.
But what if you want to pass some additional values to your Named Template? In that case, you can create a new scope object. This object will still have access to the top-level objects, and it can also contain additional values.
For instance, suppose you have a Named Template that creates a label with the name of a component, and you want to use this template for multiple components. You could define the template like this:
{{- define "mychart.componentLabel" -}}
app.kubernetes.io/component: {{ .component }}
{{- end -}}
And then you could use the template for a Service and a Deployment like this:
# For the Service
metadata:
labels:
{{- include "mychart.componentLabel" (dict "component" "webserver") | nindent 4 }}
# For the Deployment
metadata:
labels:
{{- include "mychart.componentLabel" (dict "component" "database") | nindent 4 }}
In this example, the dict function is used to create a new scope
object that includes a component field. This field is then accessed
within the Named Template to set the app.kubernetes.io/component
label. This approach allows you to reuse the same Named Template with
different values, making your Helm charts more flexible and DRY (Don’t
Repeat Yourself).
2. Using template directive
Passing values to Named Templates allows you to create more dynamic
templates that can adapt based on the input they receive. When you call
a Named Template, you pass a scope object that can contain multiple
values. This scope object is typically represented by the . character,
which includes all of the top-level objects in your Helm chart, such as
.Chart, .Release, and .Values.
Now let’s consider an example where we define a Named Template and then
call it using the template directive:
Define the Named Template: We’ll create a Named Template that generates a greeting message based on the input it receives.
{{- define "mychart.greeting" -}}
Hello, {{ .name }}!
{{- end }}
In this example, the Named Template expects the scope object to have a
name field.
Call the Named Template with the template directive: We can then
call this Named Template using the template directive and pass a
custom scope object to it:
message: {{ template "mychart.greeting" (dict "name" "Alice") }}
In this case, we use the dict function to create a new scope object
that contains a name field. The template directive then calls the
“mychart.greeting” Named Template with this scope object, and the
resulting greeting message is directly output into the parent template.
Just like the include function, the template directive allows you to
pass values to Named Templates, but it directly outputs the result of
the Named Template into the parent template. This makes it a valuable
tool for creating dynamic Helm charts.
Should you use include or template to call Named Templates?
The include and template functions in Helm are similar in that they
both allow you to call Named Templates and pass a scope to them.
However, there are a few differences that might lead you to choose one
over the other depending on the context.
1. Output Handling:
include: The include function returns the output of the template
as a string. This is useful if you need to further process or manipulate
the output. For example, you could pipe the output to another function,
or assign it to a variable.
# Using the output as a string
message: "{{ include "mychart.greeting" . }}"
# Piping the output to another function
message: {{ include "mychart.greeting" . | quote }}
template: The template function directly writes the output of
the template to the output stream. This is useful when you don’t need to
manipulate the output and you want it to be included directly in your
parent template.
# Directly writing to the parent template
message: {{ template "mychart.greeting" . }}
2. Error Handling:
include: If theincludefunction encounters an error when trying to render a template (e.g., due to a syntax error), it will return an empty string and the error. This means that your template might still render, but with parts of it missing or blank.template: If thetemplatefunction encounters an error, it stops the execution and the error is returned to the user. This means if there is an error in your Named Template, it will stop your whole Helm chart from rendering, which might be what you want if the Named Template is critical to your application.
includeovertemplatein Helm
templates simply so that the output formatting can be handled better for
YAML documents.
Control Structures in Named Templates
Control structures in Helm’s template language provide powerful ways to
manipulate the output of your Named Templates. Helm uses the Go template
language, which includes control structures like if, else, with,
and range.
1. if and else: These are used to conditionally render parts of
your template. For instance, you might want to include certain
Kubernetes
resources or configurations only when certain values are set.
Here’s an example of if and else in a Named Template:
{{- define "mychart.customLabel" -}}
{{- if .Values.customLabel }}
customLabel: {{ .Values.customLabel }}
{{- else }}
customLabel: "default"
{{- end }}
{{- end }}
In this example, if the .Values.customLabel is set, the template will
output the custom label value. Otherwise, it outputs a default label.
2. with: The with action is similar to if, but it also changes
the context within its block. This is useful when you’re accessing a
value multiple times.
Here’s an example of with in a Named Template:
{{- define "mychart.labels" -}}
{{- with .Values.labels }}
app: {{ .app }}
environment: {{ .environment }}
{{- end }}
{{- end }}
In this example, .Values.labels is checked. If it’s non-empty, the
context within the with block is changed to .Values.labels, and you
can directly access its subfields .app and .environment.
3. range: The range action allows you to iterate over arrays or
maps. It’s useful when you want to generate multiple similar lines, like
when creating multiple environment variables from a list.
Here’s an example of range in a Named Template:
{{- define "mychart.environmentVars" -}}
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value }}
{{- end }}
{{- end }}
In this example, if .Values.env is an array of objects where each
object has a name and value field, the template will generate a list
of environment variables.
Common Functions in Named Templates
Helm templates use the Go template language and come with several built-in functions that can be used to manipulate the output. Below are some commonly used functions that you may find helpful in Named Templates:
default: The default function provides a default value if a
variable is not set or is empty. Here’s an example:
{{- define "mychart.image" -}}
image: {{ default "nginx:latest" .Values.image }}
{{- end }}
In this example, if .Values.image is not set, the template will use
"nginx:latest" as the default value.
quote: The quote function wraps a string in double quotes. It’s
useful when you want to ensure a value is always treated as a string.
Here’s an example:
{{- define "mychart.label" -}}
version: {{ .Chart.Version | quote }}
{{- end }}
In this example, .Chart.Version is converted to a string by wrapping
it in double quotes.
tpl: The tpl function allows you to render strings as templates.
This is useful when you want to use a value from the .Values file as a
template that can reference other values. Here’s an example:
{{- define "mychart.message" -}}
message: {{ tpl .Values.message . }}
{{- end }}
In this example, if .Values.message is
"We are using {{ .Chart.Name }} chart", the tpl function would
render this string as a template and substitute {{ .Chart.Name }} with
the actual chart name.
nindent: The nindent function is similar to indent, but it
adds a new line before the indented text. This can be particularly
useful in YAML files, where indentation is significant and you might
want to insert a block of text with a specific indentation.
{{- define "mychart.labels" -}}
labels:
{{- include "mychart.commonLabels" . | nindent 4 }}
{{- end }}
required: The required function allows you to declare a
particular value as required for template rendering. If a required value
is not set, Helm will stop and return an error message that you provide.
{{- define "mychart.image" -}}
image: {{ required "You must provide your own image by setting the image value" .Values.image }}
{{- end }}
Example Usage with Debugging Tips
For the sake of this example, I’ll assume that you’re trying to create a simple chart for deploying a Node.js application. Here’s how you can do it:
Create a new Helm chart:
helm create mychart
This will create a new directory named “mychart” with a structure like this:
mychart/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
3 directories, 10 files
Edit the _helpers.tpl file and define a new template that calculates a
value.
{{/* Generate a random string */}}
{{- define "mychart.randomString" -}}
{{- printf "%s-%s" .Release.Name (randAlphaNum 5) | lower | trunc 63 | trimSuffix "-" -}}
{{- end -}}
Edit deployment.yaml, service.yaml, configmap.yaml and
secret.yaml (create these last two if they don’t already exist), and
use the named template to populate some of their fields.
For example, in deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "mychart.randomString" . }}
...
In NOTES.txt, you can use the named template like this:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ template "mychart.name" . }},app.kubernetes.io/instance={{ template "mychart.randomString" . }}" -o jsonpath="{.items[0].metadata.name}")
...
Finally, you can install the chart into a Kubernetes cluster with a command like this:
$ helm install my-release ./mychart
NAME: my-release
LAST DEPLOYED: Wed Jun 28 17:50:48 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=my-release-w0qb4" -o jsonpath="{.items[0].metadata.name}")
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=my-release" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
After you have deployed your Helm chart, you can inspect the resources that were created to ensure your variable was used correctly. For example, if you used the variable to set the name of a Kubernetes service, you could use the following command to see the service:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11m
my-release-wrvlf ClusterIP 10.111.183.135 <none> 80/TCP 64s
mywebapp ClusterIP 10.100.150.26 <none> 80/TCP 6m13s
Debugging Tips
When working with complex Helm charts, debugging can sometimes be a challenge. Here are some tips:
- Use
helm lint: This command can help you catch issues early on. It checks that your chart follows Helm’s best practices. - Use
helm template: This command renders your templates, which can help you catch issues before deploying. You can use it to check that your Named Templates are correctly defined and are producing the expected output. - Use
--debugand--dry-run: These flags forhelm installandhelm upgradewill show you the rendered templates and simulate an install or upgrade, respectively. They won’t make any changes to your Kubernetes cluster, but they will show you exactly what Helm would do, which can be very useful for debugging. - Inspect Kubernetes events and logs: If your application isn’t
behaving as expected, check
the events and logs of your Kubernetes resources to see if they
provide any clues. Use
kubectl describeto get a list of events for a resource, andkubectl logsto get the logs.
Summary
This article covered in-depth how to create and utilize Named Templates
in Helm3, including how to define Named Templates, how to call them
using both the include and template functions, how to pass values to
them, and how to use control structures within them. Advanced topics
such as creating reusable Helm chart libraries and using Named Templates
across multiple Helm charts were also discussed.
Key Takeaways
- Named Templates in Helm3 provide a way to define reusable chunks of code, which can help in maintaining complex Helm charts.
- Named Templates are created using the
definedirective and are usually stored in the_helpers.tplfile. - The
includeandtemplatefunctions are used to call Named Templates. The main difference between them lies in their handling of output and error. - Helm’s Go templating language supports a rich set of functions, such
as
default,quote,tpl, and others, which can be used within Named Templates. - Helm3 supports the creation of reusable library charts, which can be utilized across multiple application charts.

![Helm Named Templates in Kubernetes [In-Depth Tutorial]](/helm-named-templates/named-templates.jpg)
