Package your Kubernetes manifests into one reusable chart — install, upgrade, and roll back like a pro.
Advanced A2 · Turn the A1 manifests into a templated chart with per-environment values.
Advanced Packaging ~60 minvalues.yaml and {{ }} directiveshelm template / --dry-run, and pull charts from a repoPrerequisites: A1 (Production Kubernetes) — we package the exact manifests you built there. A running k3d cluster.
In A1 you ended up with a pile of raw YAML: db.yaml, web.yaml, hpa.yaml, rbac.yaml. Now imagine running that in dev, staging, and prod — each needs different replica counts, image tags, and resource sizes. With raw YAML you'd copy-paste and hand-edit each set. That's how environments drift apart and break.
No variables, no reuse, no versioning of "what's deployed," and no one-command rollback. Change the image tag in prod and you're editing files by hand under pressure.
Helm is the package manager for Kubernetes. You template your manifests once, put the bits that change into values.yaml, and Helm renders + installs them as a versioned release you can upgrade or roll back instantly.
A chart is a package. helm install deploys it, helm upgrade ships a new version, helm rollback reverts — each tracked with a revision number. You can also pull ready-made charts (Postgres, Prometheus, Grafana) from public repos instead of writing YAML.
| Piece | What it is |
|---|---|
| Chart | A folder of templated Kubernetes manifests + metadata = one package. |
Chart.yaml | Metadata: name, version, app version. |
values.yaml | Default configuration — the knobs users can override. |
templates/ | Your manifests with {{ }} placeholders filled from values. |
| Release | One installed instance of a chart, with a revision history. |
| Repository | A place to share charts (like Docker Hub for images). |
templates/ + values.yaml → helm install → rendered manifests applied as a release → helm upgrade / rollback manage revisions.
We'll turn the A1 manifests into a chart called notes-chart. Make sure your k3d cluster from A1 is running and the notes-app:1.0 image is imported.
The scaffold leaves Chart.yaml, values.yaml, and an empty templates/ — exactly our starting point.
notes-chart/values.yamlEverything that differs between environments lives here:
templates/web.yamlThis is the same Deployment + Service from A1, but the changing bits now read from .Values. Note {{ }} directives and the helpers toYaml/nindent:
{{ .Values.x }} inserts a value · {{ .Release.Name }} is a built-in (the release name) · {{- toYaml .Values.x | nindent N }} drops a whole YAML block in at the right indentation. The leading - trims preceding whitespace.
templates/db.yamlThe StatefulSet, Secret, and headless Service from A1, with the password and storage size templated:
helm templateAlways preview what Helm will generate. This catches template bugs without touching the cluster:
One command deployed the entire stack — web, db, secret, services — as a single tracked release. No more juggling four kubectl apply commands.
Create values-prod.yaml with only the overrides for production:
Layer it on top of the defaults with -f (later files win). Or override one value inline with --set:
One chart + values-dev.yaml / values-staging.yaml / values-prod.yaml = identical structure everywhere, with only the differences declared. This is how teams keep environments in sync.
Every install/upgrade creates a revision. If a release goes bad, revert in one command:
No hunting for old YAML or remembering what changed — Helm stores every revision and rolls back atomically. This alone is why teams adopt it.
You rarely write charts for common software — you pull battle-tested ones. For example, a production-grade Postgres:
helm uninstall notes -n notes removes the entire release in one go.
| Command | What it does |
|---|---|
helm create NAME | Scaffold a new chart |
helm lint ./chart | Check a chart for problems |
helm template REL ./chart | Render manifests locally (no cluster) |
helm install REL ./chart | Deploy a chart as a release |
helm install REL ./chart -f vals.yaml | Install with custom values |
helm install ... --set k=v | Override a single value inline |
helm upgrade REL ./chart | Ship a new revision |
helm upgrade --install REL ./chart | Install or upgrade (idempotent, great for CI) |
helm history REL | List revisions |
helm rollback REL N | Revert to revision N |
helm list / helm status REL | List releases / inspect one |
helm uninstall REL | Remove a release |
helm repo add / update | Add & refresh chart repositories |
| Symptom | Likely cause & fix |
|---|---|
nil pointer evaluating interface | A value referenced in a template is missing from values.yaml — add it (or guard with default). |
| Wrong indentation in rendered YAML | Use nindent (not indent) after a newline, and mind the {{- whitespace trim. |
cannot re-use a name that is still in use | Release already exists — helm upgrade instead of install, or uninstall first. |
| Values not taking effect | -f file order matters (last wins); --set beats files. Confirm with helm template. |
| Upgrade left things broken | helm rollback REL <previous> — that's what revisions are for. |
| Don't know what's deployed | helm get values REL and helm get manifest REL. |
min/max replicas as values.values-dev.yaml (1 replica, tiny resources) and install a separate notes-dev release in its own namespace._helpers.tpl named template for common labels and reuse it across manifests.{{- if .Values.db.enabled }} so it can be turned off.Chart.yaml and helm dependency update.Package an app as a Helm chart, template it with values, install/upgrade/roll back releases, and manage many environments from one chart. Your A1 manifests are now a clean, versioned package.
Next up: A3 — GitOps with Argo CD, where Git becomes the single source of truth: commit a change to your chart's values and the cluster syncs itself — no more running helm upgrade by hand.