helm template
pass variables
Kubernetes
Helm chart
DevOps

Pass multiple variables in helm template

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Helm templates use Go's text/template engine to render Kubernetes manifests dynamically. Variables come from values.yaml (defaults), --set flags (overrides), and custom values files (-f custom.yaml). Passing multiple variables involves accessing them through the .Values object in templates, using --set for individual overrides, and using custom values files for environment-specific configurations. For passing variables between templates and helper functions, Helm uses the dict, list, and include functions.

Passing Variables via values.yaml

yaml
1# values.yaml
2replicaCount: 3
3image:
4  repository: nginx
5  tag: "1.25"
6  pullPolicy: IfNotPresent
7service:
8  type: ClusterIP
9  port: 80
10database:
11  host: postgres.default.svc
12  port: 5432
13  name: myapp
14  credentials:
15    username: admin
16    password: secret123
yaml
1# templates/deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5  name: {{ .Release.Name }}-app
6spec:
7  replicas: {{ .Values.replicaCount }}
8  template:
9    spec:
10      containers:
11        - name: app
12          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
13          imagePullPolicy: {{ .Values.image.pullPolicy }}
14          ports:
15            - containerPort: {{ .Values.service.port }}
16          env:
17            - name: DB_HOST
18              value: {{ .Values.database.host | quote }}
19            - name: DB_PORT
20              value: {{ .Values.database.port | quote }}
21            - name: DB_NAME
22              value: {{ .Values.database.name | quote }}

Overriding with --set

bash
1# Override single values
2helm install myrelease ./mychart --set replicaCount=5
3
4# Override nested values with dot notation
5helm install myrelease ./mychart \
6  --set image.tag="2.0" \
7  --set database.host="db.prod.svc"
8
9# Override multiple values in one --set
10helm install myrelease ./mychart \
11  --set replicaCount=5,service.port=8080
12
13# Set string values explicitly (useful for numeric strings)
14helm install myrelease ./mychart --set-string image.tag="1.25"
15
16# Override with array values
17helm install myrelease ./mychart \
18  --set "ingress.hosts[0].host=example.com" \
19  --set "ingress.hosts[0].paths[0].path=/"

Using Custom Values Files

yaml
1# values-production.yaml
2replicaCount: 5
3image:
4  tag: "1.25-stable"
5database:
6  host: postgres-prod.database.svc
7  credentials:
8    username: prod_admin
9resources:
10  requests:
11    memory: 512Mi
12    cpu: 500m
13  limits:
14    memory: 1Gi
15    cpu: 1000m
bash
1# Use default values.yaml + production overrides
2helm install myrelease ./mychart -f values-production.yaml
3
4# Stack multiple values files (later files override earlier ones)
5helm install myrelease ./mychart \
6  -f values.yaml \
7  -f values-production.yaml \
8  -f values-secrets.yaml
9
10# Combine files with --set (--set takes highest priority)
11helm install myrelease ./mychart \
12  -f values-production.yaml \
13  --set image.tag="hotfix-1.25.1"

Passing Variables Between Templates

Using include with Context

yaml
1# templates/_helpers.tpl
2{{- define "mychart.labels" -}}
3app.kubernetes.io/name: {{ .name }}
4app.kubernetes.io/instance: {{ .release }}
5app.kubernetes.io/version: {{ .version }}
6{{- end }}
yaml
1# templates/deployment.yaml
2metadata:
3  labels:
4    {{- include "mychart.labels" (dict "name" .Chart.Name "release" .Release.Name "version" .Chart.AppVersion) | nindent 4 }}

Using dict to Pass Multiple Variables

yaml
1# Pass a custom dictionary to a template
2{{- define "mychart.container" -}}
3- name: {{ .name }}
4  image: "{{ .image }}:{{ .tag }}"
5  ports:
6    - containerPort: {{ .port }}
7{{- end }}
8
9# Call it with a dict
10containers:
11  {{- include "mychart.container" (dict "name" "web" "image" .Values.image.repository "tag" .Values.image.tag "port" 8080) | nindent 8 }}

Using $ to Access Root Context

Inside range loops, the context (.) changes. Use $ to access the root context.

yaml
1# templates/configmap.yaml
2apiVersion: v1
3kind: ConfigMap
4metadata:
5  name: {{ $.Release.Name }}-config
6data:
7  {{- range $key, $value := .Values.config }}
8  {{ $key }}: {{ $value | quote }}
9  {{- end }}

Variable Assignment in Templates

yaml
1# Assign variables with $varName
2{{- $fullName := printf "%s-%s" .Release.Name .Chart.Name -}}
3{{- $port := .Values.service.port | default 80 -}}
4
5metadata:
6  name: {{ $fullName }}
7spec:
8  ports:
9    - port: {{ $port }}

Common Pitfalls

  • Forgetting to quote string values: YAML interprets unquoted values like true, false, null, and numbers automatically. Use {{ .Values.tag | quote }} or --set-string to ensure values are treated as strings. Without quoting, tag: "1.0" becomes the float 1 in YAML.
  • --set priority confusion: Values cascade in order: values.yaml (lowest) < -f custom.yaml < --set (highest). Later sources override earlier ones. This means --set always wins, which can be confusing when debugging why a values file override is not taking effect.
  • Losing root context in range loops: Inside {{ range .Values.items }}, the dot (.) refers to the current item, not the root context. Accessing .Release.Name inside the loop fails. Use $.Release.Name to access the root context.
  • Not using nindent for included templates: include returns the template output as a string. Without | nindent N, the included YAML is not properly indented, causing invalid Kubernetes manifests. Always pipe to nindent with the correct indentation level.
  • Passing the wrong context to include: include "mychart.labels" . passes the current context. If called inside a range loop, . is the loop item, not the root. Use include "mychart.labels" $ or construct a dict with the specific values needed.

Summary

  • Access variables in templates via {{ .Values.key }} from values.yaml
  • Override with --set key=value (highest priority) or -f custom-values.yaml
  • Use dict to pass multiple named variables to include template functions
  • Use $ to access root context inside range loops
  • Always quote string values with | quote to prevent YAML type coercion

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design