1. Charts, Templates & values.yaml
A chart is a package of Kubernetes manifests written as templates,
with placeholders filled in from a values.yaml file. Think of it as a
parameterized version of everything you've written in Weeks 8–12.
my-app/
├── Chart.yaml # chart metadata: name, version, description
├── values.yaml # DEFAULT values — every template placeholder's fallback
└── templates/
├── deployment.yaml # a Deployment, with {{ .Values.X }} placeholders
├── service.yaml
└── ingress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Chart.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
{{- toYaml .Values.resources | nindent 12 }}
replicaCount: 3
image:
repository: myrepo/my-app
tag: "1.5.0"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
{{ .Values.X }} pulls from values.yaml at render time.
This is the whole idea of Helm in one example: the exact same template renders
differently just by changing what's in values.yaml — no manifest is
ever hand-edited per environment again.
helm template my-app ./my-app prints the fully-rendered YAML without touching your cluster at all — invaluable for catching a syntax mistake before it ever reaches kubectl apply or a live release.
2. Installing a Public Chart
Most popular software you'd self-host on Kubernetes — Postgres, Redis, Prometheus, cert-manager itself — already has a well-maintained public chart, saving you from writing dozens of manifests by hand.
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community
helm install my-prometheus prometheus-community/prometheus
helm list # see every installed release
helm status my-prometheus # detailed status of one
helm uninstall my-prometheus
Before installing blindly, it's worth inspecting what a chart will actually create:
helm show values prometheus-community/prometheus # see every configurable option
helm template my-prometheus prometheus-community/prometheus | less # see rendered manifests
helm install my-prometheus prometheus-community/prometheus --dry-run --debug
The Prometheus + Grafana observability stack you'll deploy for monitoring is, in practice, almost always installed via Helm rather than hand-written manifests — this section is direct preparation for that.
3. Writing Your Own Chart
Scaffold and build a chart for your own app:
helm create my-app
# generates a starter chart with sensible defaults you'll trim down or extend
cd my-app
helm template . | less # preview the rendered output
helm install my-release . --dry-run --debug # validate without actually installing
helm install my-release . # install for real
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
spec:
selector:
app: {{ .Chart.Name }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
type: {{ .Values.service.type }}
Notice the label selector, {{ .Chart.Name }}, matches the Deployment
template's Pod labels exactly — the same selector-matching principle from Week 9,
just generated by a template instead of typed twice by hand where it could drift
out of sync.
The scaffolded chart includes HPA, Ingress and ServiceAccount templates you may not need yet — trim what you don't use rather than shipping unused, half-understood YAML. Understanding every template you keep matters more than keeping everything the scaffold generated.
4. Releases & Per-environment Values
A Helm release is one installed instance of a chart — you can install the same chart multiple times, under different release names, with different values, side by side.
# values-dev.yaml
replicaCount: 1
image:
tag: "dev-latest"
resources:
requests: { cpu: "50m", memory: "64Mi" }
# values-prod.yaml
replicaCount: 5
image:
tag: "1.5.0"
resources:
requests: { cpu: "250m", memory: "256Mi" }
limits: { cpu: "1000m", memory: "512Mi" }
helm install my-app-dev . -f values-dev.yaml --namespace dev
helm install my-app-prod . -f values-prod.yaml --namespace prod
# upgrading after a change — this is your Week 8 rolling update, now Helm-driven:
helm upgrade my-app-prod . -f values-prod.yaml --set image.tag=1.6.0
helm rollback my-app-prod 1 # roll back to release revision 1
helm history my-app-prod # see every past revision, just like kubectl rollout history
helm upgrade and helm rollback are Helm's layer on top of
the exact rolling-update and rollback mechanics from Week 8 — Helm tracks its own
release history in addition to what Kubernetes tracks natively, giving you one
command that manages every resource in the chart together, atomically.
Week 17's GitOps setup deploys via exactly this pattern — a chart plus per-environment values files, committed to Git, with ArgoCD triggering helm upgrade automatically whenever those files change.
5. Hands-on Exercise
Turn your app's manifests into a chart, then deploy it to two "environments"
Convert the raw YAML from earlier weeks into a proper Helm chart, then use it to deploy two differently-configured releases of the same app side by side.
Part 1 — Build the chart:
- Scaffold a chart with
helm create, then replace its templates/deployment.yaml and templates/service.yaml with templated versions of your own app's manifests from Weeks 8–9. - Move every value that should vary (image tag, replica count, resource limits) into
values.yamlas placeholders. - Run
helm template .and confirm the rendered output looks correct — a real, valid Deployment and Service manifest with your values substituted in. - Install it for real with
helm install, confirm it works, thenhelm uninstallit cleanly.
Delete the scaffolded templates you're not using (HPA, ServiceAccount, tests) rather than leaving them half-configured — an unused template with a broken reference to a values.yaml key you removed is a common source of confusing render errors.
Part 2 — Two environments, one chart:
- Create
values-dev.yamlandvalues-prod.yamlwith meaningfully different replica counts and resource requests. - Create two namespaces (
kubectl create namespace dev,kubectl create namespace prod) and install your chart into each, using the matching values file and a distinct release name. - Confirm with
kubectl get deployments -n devand-n prodthat replica counts differ as expected between the two. - Bump the image tag in
values-prod.yamlonly, runhelm upgradeagainst the prod release, and confirm withhelm historythat a new revision was recorded — then practice ahelm rollbackback to the previous one.
Add -n <namespace> to every helm and kubectl command in this exercise — it's easy to accidentally run a command against the default namespace and wonder why your dev/prod resources "aren't there."
6. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What's the relationship between a chart's templates and its values.yaml?
What's the relationship between a chart's templates and its values.yaml?
Templates are Kubernetes manifests with placeholders like {{ .Values.X }}. values.yaml supplies the actual values those placeholders resolve to at render time. The same templates render into different manifests depending only on which values file (or overrides) is used.
Q2
What does helm template . do, and why is it useful before a real install?
What does helm template . do, and why is it useful before a real install?
It renders the chart's templates into final Kubernetes YAML locally, without touching the cluster at all. It's a fast way to catch templating mistakes or unexpected output before running helm install, which actually creates resources.
Q3
Can the same chart be installed multiple times in the same cluster with different configurations?
Can the same chart be installed multiple times in the same cluster with different configurations?
Yes — each installation is a separate "release" with its own name, typically in its own namespace, and can be given a completely different values file. This is exactly how one chart deploys differently-configured dev and prod releases side by side.
Q4
What does helm rollback do, at a mechanical level?
What does helm rollback do, at a mechanical level?
It re-applies a previous revision's rendered manifests for every resource the chart manages, restoring the release (Deployment, Service, ConfigMap, etc. together) to that earlier state in one atomic operation — the Helm-level equivalent of kubectl rollout undo, but covering the whole chart's resources at once.