Helm for Beginners

Helm Charts Anatomy

Named Templates

In this lesson, you'll learn how to use named templates to eliminate repetitive code in your Helm charts. When creating Kubernetes manifests, you might notice that labels or other blocks often repeat across multiple objects. For instance, consider the following YAML snippets for a Service and a Deployment where identical label definitions appear in several sections:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
spec:
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: hello-world
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: nginx
      app.kubernetes.io/instance: nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx
        app.kubernetes.io/instance: nginx
    spec:
      containers:
        - name: nginx
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

As your codebase expands, duplicating these definitions increases the risk of errors and inconsistencies during updates.


Tip

Keep your Helm charts DRY (Don't Repeat Yourself) by consolidating common code blocks in a helper file.

Moving Common Labels to a Helper File

To address this redundancy, you can transfer the shared lines to a helper file (commonly named _helpers.tpl). The underscore in the filename tells Helm to ignore this file when generating Kubernetes manifests. For example, if you start with a Service template like this:

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    # common labels will be included here
spec:
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: hello-world

You can move the repeated labels into a named template using the define directive. In your _helpers.tpl, add:

{{- define "labels" }}
  app.kubernetes.io/name: {{ .Release.Name }}
  app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

Next, update your Service manifest to include the named template:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    {{- template "labels" . }}
spec:
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: hello-world

Notice that appending a dot (.) to the template call passes the current context into the helper file. Without it, the helper template wouldn’t have access to critical values like .Release.Name.


Applying Named Templates in Deployment Manifests

The same approach works for Deployment manifests where the labels are used in multiple locations. Initially, you might attempt to reuse the helper template like this:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    {{- template "labels" . }}
spec:
  selector:
    matchLabels:
      {{- template "labels" . }}
  template:
    metadata:
      labels:
        {{- template "labels" . }}
    spec:
      containers:
      - name: nginx
        image: "nginx:1.16.0"
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80
          protocol: TCP

However, this method may result in unexpected indentation issues. When the helper template is inserted, it must respect the surrounding indentation. Although the first instance might work correctly if the helper template is properly formatted, additional instances may not be aligned as expected.

Using the include Function for Proper Indentation

To resolve the indentation problem, use the include function instead of template. The include function allows the output to be piped to other functions like indent. Ensure your helper template in _helpers.tpl is defined as follows:

{{- define "labels" }}
  app.kubernetes.io/name: {{ .Release.Name }}
  app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

Then, update the Deployment manifest to properly indent the inserted template output:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
  labels:
    {{- template "labels" . }}
spec:
  selector:
    matchLabels:
      {{- include "labels" . | indent 2 }}
  template:
    metadata:
      labels:
        {{- include "labels" . | indent 4 }}
    spec:
      containers:
        - name: nginx
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

This practice ensures that the output from the named template “labels” remains correctly indented, preserving readability and consistency in your generated manifest.


Final Example

Below is an example of the final output with actual values replacing the template directives:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: RELEASE-NAME-nginx
  labels:
    app.kubernetes.io/name: nginx-chart
    app.kubernetes.io/instance: nginx-release
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: nginx-chart
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx-chart
        app.kubernetes.io/instance: nginx-release
    spec:
      containers:
        - name: nginx
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

This approach of moving repetitive code into named templates and using functions like include with proper indentation not only enhances code maintainability but also minimizes the risk of errors and inconsistencies in your Helm charts.


For more details on managing Helm charts and Kubernetes manifests, visit the Helm Documentation and Kubernetes Concepts.

Watch Video

Watch video content

Previous
Ranges