Lab Guide: Templating Fundamentals
Lesson 03 — 3 exercises, ~45 minutes
Prerequisites
- Helm 3.x installed (
helm version) - A working Kubernetes cluster (minikube, kind, or cloud) or just
helm templatefor local rendering - Completed Lesson 02 (Your First Chart)
Exercise 1: Values and Pipelines
Create a chart that uses .Values, .Release, and .Chart objects with pipeline functions.
Step 1 — Create a fresh chart
helm create templating-lab
cd templating-lab
Step 2 — Replace values.yaml
Overwrite values.yaml with the following content:
# values.yaml
appName: my-web-app
replicaCount: 2
image:
repository: nginx
tag: "1.25"
environment: staging
logLevel: debug
Step 3 — Create a ConfigMap template
Create the file templates/configmap.yaml (delete any existing template files first for a clean workspace):
rm templates/*.yaml templates/*.txt 2>/dev/null
cat > templates/configmap.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-config
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
data:
APP_NAME: {{ .Values.appName | upper | quote }}
ENVIRONMENT: {{ .Values.environment | default "production" | quote }}
LOG_LEVEL: {{ .Values.logLevel | default "info" | upper | quote }}
REPLICA_INFO: {{ printf "Running %d replicas" (.Values.replicaCount | int) | quote }}
EOF
Step 4 — Render the template
helm template my-release . --namespace demo
✓ Expected Output:
---
# Source: templating-lab/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-release-config
namespace: demo
labels:
app.kubernetes.io/name: templating-lab
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/managed-by: Helm
data:
APP_NAME: "MY-WEB-APP"
ENVIRONMENT: "staging"
LOG_LEVEL: "DEBUG"
REPLICA_INFO: "Running 2 replicas"
Step 5 — Test with overrides
Verify that --set overrides work and the default function activates when a value is missing:
helm template my-release . --set environment="" --set logLevel=""
✓ Expected Output (data section):
data:
APP_NAME: "MY-WEB-APP"
ENVIRONMENT: "production"
LOG_LEVEL: "INFO"
REPLICA_INFO: "Running 2 replicas"
Key Takeaway
Pipelines read left-to-right: .Values.logLevel | default "info" | upper first falls back to "info" if empty, then uppercases the result.
Exercise 2: Control Structures
Add conditional resources and iteration to your chart.
Step 1 — Update values.yaml
Add the following to the bottom of values.yaml:
ports:
- name: http
containerPort: 80
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
env:
- name: DATABASE_URL
value: postgres://db:5432/app
- name: CACHE_TTL
value: "300"
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
ingress:
enabled: true
host: myapp.example.com
Step 2 — Create a Deployment template with range
cat > templates/deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-app
spec:
replicas: {{ .Values.replicaCount | default 1 }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
{{- range .Values.ports }}
- name: {{ .name }}
containerPort: {{ .containerPort }}
protocol: {{ .protocol | default "TCP" }}
{{- end }}
{{- if .Values.env }}
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
EOF
Step 3 — Create a conditional Ingress template
cat > templates/ingress.yaml <<'EOF'
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-ingress
spec:
rules:
- host: {{ .Values.ingress.host | default (printf "%s.local" .Release.Name) }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-svc
port:
number: 80
{{- end }}
EOF
Step 4 — Render and verify
helm template my-release .
✓ Expected: Deployment ports section
ports:
- name: http
containerPort: 80
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
✓ Expected: Deployment env section
env:
- name: DATABASE_URL
value: "postgres://db:5432/app"
- name: CACHE_TTL
value: "300"
✓ Expected: Resources section
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
Step 5 — Disable ingress and verify it disappears
helm template my-release . --set ingress.enabled=false | grep -c "kind: Ingress"
✓ Expected Output:
0
Exercise 3: Whitespace Control
Practice the difference between trimmed and untrimmed template actions.
Step 1 — Create a whitespace test template
cat > templates/whitespace-test.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-ws-test
labels:
{{- if .Values.appName }}
app: {{ .Values.appName }}
{{- end }}
{{- with .Values.environment }}
env: {{ . }}
{{- end }}
chart: {{ .Chart.Name }}
data:
{{- range $key, $val := .Values.env }}
{{ $key }}_KEY: {{ $val.name | quote }}
{{- end }}
EOF
Step 2 — Render and inspect whitespace
helm template my-release . -s templates/whitespace-test.yaml
✓ Expected Output:
---
# Source: templating-lab/templates/whitespace-test.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-release-ws-test
labels:
app: my-web-app
env: staging
chart: templating-lab
data:
0_KEY: "DATABASE_URL"
1_KEY: "CACHE_TTL"
Step 3 — Experiment: remove trim markers
Edit the template and remove all - trim markers from {{-. Re-render and observe the blank lines that appear in the output. Then restore the markers.
# See the difference
sed 's/{{-/{{/g' templates/whitespace-test.yaml > /tmp/no-trim.yaml
cp /tmp/no-trim.yaml templates/whitespace-test-notrim.yaml
helm template my-release . -s templates/whitespace-test-notrim.yaml
rm templates/whitespace-test-notrim.yaml
Observe
Without trim markers you will see extra blank lines in the labels and data sections. These blank lines are harmless for ConfigMaps but can break stricter YAML parsers and make output hard to read.
Cleanup
cd ..
rm -rf templating-lab
Lab Complete
You have practiced value interpolation with pipelines, conditional resources with if, scope changes with with, iteration with range, the toYaml | nindent pattern, and whitespace control. Continue to the Homework for additional challenges.