Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 74

Dynamic Manifests with Helm Templates

Recommended resources

Hello! Welcome back to our module on Kubernetes Ecosystem and Operations.

In the last lesson, we took a significant step forward by packaging an application into a Helm chart. You learned to templatize manifests by substituting static values with references to values.yaml, and how to use environment-specific values files to customize deployments. This is the foundation of managing Kubernetes applications at scale.

However, simple value substitution has its limits. What if you need to create a resource only in a specific environment? Or iterate over a list of configuration items? This is where Helm's more advanced templating capabilities come into play.

This lesson directly addresses that need by focusing on the learning outcome: Use Helm template functions and control structures (if/else, range) to dynamically generate manifests. We'll move beyond basic placeholders and unlock the full power of the Go templating engine that Helm uses.

By the end of this lesson, you will be able to:

  • Use functions and pipelines to transform values.
  • Implement conditional logic with if/else blocks.
  • Iterate over lists and maps using range.
  • Create reusable template snippets with _helpers.tpl.

1. Helm Functions and Pipelines

At the heart of Helm's templating engine is the concept of functions and pipelines. You've already used simple value access like {{ .Values.replicaCount }}. A pipeline allows you to take that value and pass it through a series of functions, where the output of one becomes the input of the next. The pipe character | is used for this.

For example:
{{ .Values.appName | upper | quote }}

This pipeline does three things:

  1. It retrieves the value of appName from values.yaml.
  2. It passes this value to the upper function, which converts it to uppercase.
  3. The result of upper is then passed to the quote function, which wraps it in double quotes, ensuring it's a valid YAML string.

Helm includes all of the functions from Go's Sprig library, giving you a vast toolkit for string manipulation, math, type conversions, and more.

Mastering Helm Template Functions and Pipelines

The article "Mastering Helm Template Functions and Pipelines" from OneUptime is an excellent reference for the functions available. You don't need to memorize them all, but it's good to be aware of what's possible.

Please review the sections on Pipelines and String Functions. This will give you a solid idea of how functions are chained together and the common operations you can perform on strings.


2. Conditional Logic with if/else

One of the most common requirements in advanced charting is conditional logic. For example, you might want to create an Ingress resource only if a specific value is set to true in your values.yaml. This is handled with if/else blocks.

The basic syntax is:

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Release.Name }}-ingress
spec:



  # ... ingress spec
{{- end }}
  • {{- if PIPELINE }}: The block is rendered only if the pipeline evaluates to true. In Helm, false, 0, nil, and empty strings/lists/dicts are considered false. Everything else is true.
  • {{- end }}: This closes the if block.
  • {{- and -}}: Notice the hyphens. These are for whitespace control. {{- removes whitespace to its left, and -}} removes it to its right. Using them is crucial for generating clean, valid YAML.

You can also build more complex logic with else if and else, using comparison functions like eq (equal), ne (not equal), gt (greater than), etc.

Helm Flow Control: If/Else and eq Function
This image shows a practical example of an `if/else` structure. Based on the value of `.Values.environment`, it uses the `eq` function to conditionally set the `replicas` to different values for 'prod' and 'qa' environments.

To dive deeper into the syntax and rules, the official Helm documentation is the best source.

Flow Control - Helm

The official Helm documentation provides a clear explanation of flow control structures.

Please read the sections If/Else and Controlling Whitespace. Pay close attention to how values are evaluated as true or false and the use of {{- to manage YAML indentation.


3. Iterating with range

The range action allows you to loop through collections like lists (slices) and dictionaries (maps). This is incredibly useful for dynamically generating repeated blocks of YAML.

Ranging over a List

Imagine you want to define multiple container ports from a list in values.yaml:

values.yaml:

ports:
  - name: http
    containerPort: 80
  - name: metrics
    containerPort: 9100

deployment.yaml template:

ports:
{{- range .Values.ports }}
- name: {{ .name }}
  containerPort: {{ .containerPort }}
{{- end }}

Inside the range block, the dot (.) is scoped to the current item in the iteration. In the first loop, . refers to the first port object ({name: http, containerPort: 80}), and so on.

Ranging over a Dictionary

You can also iterate over key-value pairs in a map, which is perfect for generating labels or annotations:

values.yaml:

podAnnotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "9100"

deployment.yaml template:

spec:
  template:
    metadata:
      annotations:
      {{- range $key, $value := .Values.podAnnotations }}
        {{ $key }}: {{ $value | quote }}
      {{- end }}

Here, range assigns the key to $key and the value to $value for each item in the map.

Flow Control - Helm

Let's return to the official Helm documentation to understand the range action.

Read the section Looping with the range action. Focus on how the scope of . changes within the loop and the different ways range can be used for lists and maps.


4. Reusable Logic with Named Templates

As your charts grow, you'll often find yourself repeating the same snippets of code. A common example is the block of labels applied to every resource. Instead of copying and pasting, you can define this logic once in a named template and include it wherever needed.

These reusable templates are traditionally defined in the _helpers.tpl file. The define action creates the template, and include renders it.

helper.tpl in HELM chart

This short video from Infrasity provides a concise explanation of what _helpers.tpl is for and how to use it.

Watch the segments explaining what helpers.tpl is (00:39 - 02:23), the syntax for defining a template (02:36 - 04:20), and how to call it using template or include (04:20 - 05:01).


5. Putting It All Together: A Comprehensive Example

Now that you understand the individual components, let's see how they work together in a realistic scenario. The following video demonstrates how to build a single, flexible Helm chart that can be deployed across multiple environments with significantly different configurations.

This walkthrough is dense but extremely practical for your role as a DevOps Engineer. It covers:

  • Using if/end to conditionally enable/disable the HPA.
  • Using named templates (include) and nindent for reusable labels.
  • Rendering entire YAML blocks from values.yaml using toYaml.
  • Dynamically selecting and rendering entire configuration files using variables, printf, and the .Files.Get function.

Single Helm Chart, Multiple environments

The video "Single Helm Chart, Multiple environments" by Deekshith SN is a masterclass in dynamic templating. It shows how to solve real-world configuration challenges.

Watch the following key segments. Pay close attention to the templating syntax being used to solve each problem: Rendering YAML Blocks (toYaml) (10:24 - 13:00): See how an entire resources block is defined in values.yaml and rendered into the template using the toYaml and nindent functions. This is a powerful pattern for complex, nested configurations. Using Named Templates (include) (13:00 - 17:12): Observe how a common set of labels is defined once in _helpers.tpl and then reused across different manifests using include. Conditional Resource Creation (if) (17:12 - 18:34): This shows how a boolean in values.yaml is used with an if block to control whether the HPA manifest is generated at all. Managing External Config Files (26:01 - End): This is an advanced but very common use case. Watch how the chart is designed to handle both YAML and non-YAML (XML) configuration files that vary per environment. Notice the use of toYaml for embedded YAML and the combination of printf and .Files.Get for external files. This demonstrates true dynamic manifest generation.


6. Debugging Your Dynamic Templates

As templates become more complex, debugging becomes essential. You need to be able to see the final rendered YAML before applying it to a cluster.

The helm template and helm install --dry-run commands are your best friends here.

Helm Debug Command Reference
This image provides a handy cheat sheet for common Helm debugging commands. `helm template . --debug` is particularly useful for rendering your chart locally and inspecting the output.

A common debugging technique is to temporarily use the fail function to print out a value and stop the rendering process: {{ .Values | toPrettyJson | fail "Dumping values" }}. This will print the entire .Values object in a readable format.



Conclusion

You have now moved from basic value substitution to advanced dynamic manifest generation. By mastering functions, pipelines, and control structures, you can create professional, reusable, and highly flexible Helm charts that can adapt to any environment or configuration requirement.

Key Takeaways:

  • Pipelines (|) chain functions together to transform data.
  • if/else blocks provide conditional logic to include or exclude parts of a template based on values.
  • range iterates over lists and maps, allowing you to generate repetitive YAML structures dynamically.
  • Named templates (define/include) in _helpers.tpl promote code reuse and maintainability.
  • Functions like toYaml and .Files.Get enable you to manage complex configuration blocks and entire files within your chart.

What's Next?

You've now mastered how to package and configure your applications with Helm. The next logical step is to automate the deployment of these packages. In the next lesson, we will explore the principles of GitOps and use ArgoCD to automatically deploy an application from a Git repository whenever changes are made. This will connect your packaging skills to a modern, declarative deployment workflow.