Skip to main content

Implementing Advanced Templating

1. Creating Recursive Templates

Recursive templates in Helm allow you to define templates that can call themselves, enabling complex nested structures like trees or hierarchical data rendering.

Template Description Usage
{{ include "mychart.recursive" . }} Include a recursive template Use to render nested structures

Example: Recursive Template for Tree Structure

{{- define "mychart.recursive" -}}
{{- if .children }}
<ul>
  {{- range .children }}
  <li>{{ .name }}
    {{- include "mychart.recursive" . }}
  </li>
  {{- end }}
</ul>
{{- end }}
{{- end -}}
Note: Recursive templates must define a base case to prevent infinite loops.

2. Building Dynamic Names

Property Description Example
nameOverride Overrides the default name of the chart nameOverride: my-app
fullnameOverride Overrides the full name of the chart, including release name fullnameOverride: my-release-my-app
name Used to construct the full name of resources name: nginx

Example: Using nameOverride

nameOverride: frontend

Example: Using fullnameOverride

fullnameOverride: production-frontend
Note: The nameOverride and fullnameOverride values are useful for customizing resource names without modifying the chart's templates.

3. Implementing Feature Flags

Flag Name Values Description Default
feature.flag1 true, false Enables experimental feature 1 false
feature.flag2 true, false Enables experimental feature 2 false

Example: Using Feature Flags in Values

feature:
  flag1: true
  flag2: false

Example: Conditional Template Rendering

<{{- if .Values.feature.flag1 }}>
  <div>Experimental Feature 1 Enabled</div>
<{{- end }}>
Note: Feature flags can be toggled at runtime via helm upgrade with updated values.

4. Creating Environment-Aware Templates

Environment-aware templates in Helm allow you to customize chart behavior based on the target environment (e.g., dev, staging, prod). This is typically achieved using values files and conditional logic within templates.

Feature Description Usage
values.yaml Defines default values for templates. Environment-specific overrides can be provided in separate files. environment: production
.Values Accesses values from the values files in templates. {{ .Values.environment }}
.Release Provides release-specific information like name and namespace. {{ .Release.Name }}

Example: Environment-specific ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
data:
  environment: {{ .Values.environment }}
  logLevel: {{ .Values.logLevel }}

Example: Conditional Deployment

{{- if eq .Values.environment "production" }}
replicas: 3
{{- else }}
replicas: 1
{{- end }}
Note: Use .Values.environment to dynamically adjust configurations based on the environment. This allows for a single chart to be reused across multiple environments with minimal changes.

5. Using Template Variables

Variable Type Description Example
.Values Object Root values passed to the chart {{ .Values.replicaCount }}
.Chart Object Chart metadata (name, version, etc.) {{ .Chart.Name }}
.Release Object Release information (name, namespace, etc.) {{ .Release.Name }}
.Capabilities Object Kubernetes version and features supported {{ .Capabilities.KubeVersion.Major }}
.Files Object Access to files in the chart {{ .Files.Get "config.yaml" }}
.Template Object Template information (name, base path) {{ .Template.Name }}

Example: Accessing Release Name

{{ .Release.Name }}

Example: Using Chart Metadata

Chart Name: {{ .Chart.Name }}
Chart Version: {{ .Chart.Version }}
Note: Template variables are case-sensitive and must be accessed using the correct syntax.

6. Implementing Complex Data Structures

Example: Using Maps and Lists in Templates

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include &#34;myapp.fullname&#34; . }}
data:
  config.yaml: |
    database:
      host: {{ .Values.database.host }}
      port: {{ .Values.database.port }}
      credentials:
        username: {{ .Values.database.credentials.username }}
        password: {{ .Values.database.credentials.password }}
    services:
      - name: frontend
        replicas: {{ .Values.services.frontend.replicas }}
        image: {{ .Values.services.frontend.image }}
      - name: backend
        replicas: {{ .Values.services.backend.replicas }}
        image: {{ .Values.services.backend.image }}
Structure Description Usage
.Values.database Map containing database configuration Used to define connection details for databases
.Values.services List of service definitions Used to define multiple services with similar structure
.Values.services.frontend.replicas Number of replicas for frontend service Used to scale services based on resource needs
Note: When using complex data structures, ensure that all keys are properly escaped to avoid YAML parsing errors.

Example: Nested Maps in Values

database:
  host: localhost
  port: 5432
  credentials:
    username: admin
    password: secret
  ssl:
    enabled: true
    mode: require

Example: List of Services with Variables

services:
  - name: frontend
    replicas: 2
    image: nginx:latest
    env:
      - name: ENV
        value: production
  - name: backend
    replicas: 3
    image: node:18
    env:
      - name: NODE_ENV
        value: production

7. Building Resource Templates Dynamically

Template Function Description Usage Example
include Includes another template file {{ include "my-template" . }}
required Ensures a value is provided or fails with error {{ required "A valid image tag is required" .Values.image.tag }}
tpl Processes a template string with values {{ tpl "{{.Values.image.tag}}" . }}

Example: Using include for Reusable Templates

{{- define "myapp.labels" -}}
app: {{ include "myapp.name" . }}
version: {{ .Values.image.tag }}
{{- end -}}

{{- define "myapp.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.name" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        {{- include "myapp.labels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ include "myapp.name" . }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
{{- end -}}

Example: Using tpl for Dynamic Template Content

{{- $customEnvVars := tpl "{{.Values.customEnvVars}}" . -}}
env:
{{- range $key, $val := .Values.env }}
  - name: {{ $key }}
    value: {{ $val | quote }}
{{- end }}
{{- if $customEnvVars }}
  {{- $customEnvVars | nindent 2 }}
{{- end }}
Note: The tpl function allows for dynamic template processing, which is useful when you need to inject templates or strings that contain template syntax.

8. Using Template Includes Efficiently

Feature Description Usage
include Template function to include content from another template file {{ include "my-template" . }}
tpl Template function to execute a template string within a template {{ tpl "{{ .Values.name }}" . }}

Example: Including a Common Header

{{ include "common.headers" . }}

Example: Using tpl for Dynamic Content

{{ tpl "{{ .Values.app.name }}" . }}
Note: Template includes are processed at render time, not during chart packaging.
Warning: Avoid deeply nested includes as they can impact performance and readability.

9. Handling Edge Cases

Example: Default Values for Missing Values

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "myapp.fullname" . }}
data:
  config.yaml: |
    database:
      host: {{ .Values.database.host | default "localhost" }}
      port: {{ .Values.database.port | default 5432 }}
Edge Case Description Solution
Missing Values Values not provided in values.yaml or overrides Use | default "value" to provide fallbacks
Empty Strings Values provided as empty strings Use | default "value" or | coalesce
Null Values Values explicitly set to null Use .Values.key | default "fallback" or .Values.key | default ""
Note: The | default function is a powerful way to handle missing or null values in templates.

Example: Conditional Logic for Missing Values

apiVersion: v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
spec:
  replicas: {{ if .Values.replicaCount }}
    {{ .Values.replicaCount }}
  {{ else }}
    1
  {{ end }}

Comparison: Default vs Coalesce

  • | default: Returns the first non-empty value (empty string, zero, false)
  • | coalesce: Returns the first non-nil value (nil, empty string, zero, false)

10. Creating Maintainable Templates

Template Type Description Best Practices
Reusability Templates designed to be reused across multiple releases or environments Use include and import directives to modularize templates
Parameterization Templates that accept values through values.yaml Define default values and use {{ .Values }} to access them
Conditional Logic Templates that conditionally render resources based on values Use {{ if }} and {{ unless }} to control rendering

Example: Reusable Template for ConfigMap

<!-- templates/configmap.yaml -->
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "myapp.fullname" . }}
data:
  config.properties: |
    server.port={{ .Values.server.port }}
    database.url={{ .Values.database.url }}

Example: Conditional Deployment

<!-- templates/deployment.yaml -->
{{- if .Values.deployment.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ include "myapp.name" . }}
{{- end }}
Note: Use {{ include }} to reference other templates and keep logic centralized.
Warning: Avoid complex logic inside templates. Keep templates simple and move complex logic to helpers.tpl.

Was this article helpful?