Skip to content

Helm & Packaging

Count what one real application needs by now: a Deployment, a Service, an Ingress, a ConfigMap, a Secret, maybe a PVC and an HPA. That’s seven-plus YAML files for one service. Now run it in three environments — dev, staging, production — that differ only in replica counts, image tags, and hostnames. The naive answer is to copy all seven files three times and hand-edit the differences.

manifests/
├── dev/ deployment.yaml service.yaml ingress.yaml ... (7 files)
├── staging/ deployment.yaml service.yaml ingress.yaml ... (7 files, 90% identical)
└── prod/ deployment.yaml service.yaml ingress.yaml ... (7 files, 90% identical)

This is a maintenance trap. Change the container port and you must edit it in three places without missing one. Staging and prod drift apart because someone fixed a bug in one and forgot the others. It’s the copy-paste-and-pray problem this whole book exists to kill — back at the cluster’s front door.

Helm is the answer, and the right mental model is the one you already have from package management in Part 1. apt installs a package — a versioned bundle plus the metadata to install, upgrade, and remove it cleanly. Helm does the same for Kubernetes:

Linux (apt)Helm
packagechart
installed programrelease
package repositorychart repository
apt installhelm install
apt upgradehelm upgrade

A chart is your set of manifests turned into a reusable, parameterized package. Install it and you get a release — a named, versioned instance of that chart running in the cluster. Suddenly your app is one installable, upgradable, removable unit instead of a pile of loose files.

The core trick is that Helm manifests are templates. The parts that vary become placeholders filled in from a values file. You write the structure once and supply the differences as data.

A chart’s layout:

myapp/
├── Chart.yaml # name + version of the chart itself
├── values.yaml # default values (the parameters)
└── templates/
├── deployment.yaml # templated manifests
├── service.yaml
└── ingress.yaml

The template references values with {{ ... }} instead of hardcoding:

templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web
spec:
replicas: {{ .Values.replicaCount }} # filled from values
template:
spec:
containers:
- name: web
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
# values.yaml — the defaults
replicaCount: 2
image:
repository: myapp
tag: "1.4.2"

Now the three environments are one chart plus three small values files — the duplication collapses to just the things that genuinely differ:

# values-prod.yaml — overrides only
replicaCount: 10
image: { tag: "1.4.2" }
Terminal window
# Same chart, different values per environment
helm install web ./myapp -f values-prod.yaml
helm template web ./myapp -f values-prod.yaml # render locally to see the YAML

Releases: upgrade and roll back as one unit

Section titled “Releases: upgrade and roll back as one unit”

Here’s where Helm earns its place over plain kubectl apply. Because a release is versioned, Helm tracks the history of what was deployed and can move you forward or backward atomically — the whole app, all its objects, as a single transaction.

Terminal window
helm install web ./myapp # revision 1
helm upgrade web ./myapp --set image.tag=1.5.0 # revision 2
helm history web # see every revision and its status
helm rollback web 1 # snap the entire release back to revision 1
helm uninstall web # remove all of the app's objects at once
helm history web
REVISION STATUS CHART DESCRIPTION
1 superseded myapp-0.1.0 Install complete
2 deployed myapp-0.2.0 Upgrade complete
bad deploy? ──────┘ helm rollback web 1 → back to a known-good state

Compare the manual alternative: a bad change means hand-reverting each of seven files and kubectl apply-ing them in the right order, under pressure, hoping you didn’t miss one. helm rollback makes “undo the deploy” a single, reliable command — which is exactly the kind of fast, safe recovery the DORA metrics reward.

Charts are also shareable. Package a chart and push it to a repository and anyone can install your app — or you can install someone else’s. This is why you rarely deploy infrastructure like Prometheus or an ingress controller by hand anymore; you helm install a community chart:

Terminal window
helm package ./myapp # → myapp-0.2.0.tgz, a portable artifact
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prom prometheus-community/kube-prometheus-stack # a whole monitoring stack, packaged

The thread, one last time for this Part: what manual, error-prone step does this remove? Without Helm, multi-environment deployment is copy-pasted YAML that silently drifts, edits repeated across files where one omission ships a bug, and rollback as a frantic manual re-apply. Helm collapses the duplication into one templated chart plus small per-environment values, so dev and prod stay structurally identical and differ only where you intend. It makes a deploy a versioned release you can upgrade and — crucially — roll back with one command, turning recovery from a risky scramble into a routine. The copy-paste drift and the white-knuckle rollback both disappear. That closes Part 4: you can now declare, expose, configure, persist, scale, and package an application on Kubernetes — and you’ve seen the same idea underneath all of it, the control loop closing the gap between what you declared and what’s running.

Step back from charts and values and judge Helm as a packaging choice:

  • Why does it exist? Because one app is seven-plus manifests, and running it across dev/staging/prod the naive way means copying all of them per environment and hand-editing the few differences — a copy-paste-and-pray maintenance trap that silently drifts.
  • What problem does it solve? It makes a Kubernetes app one installable, upgradable, removable unit: templated manifests filled from a values file collapse the duplication to the bits that genuinely differ (replica counts, image tags, hostnames), and a versioned release gives you helm rollback as one atomic command — the fast, safe recovery the DORA metrics reward.
  • What are the trade-offs? Templating is text substitution, so a placeholder can hide a typo — you must helm template/--dry-run to eyeball the rendered YAML before it hits production.
  • When should I avoid it (or pair it)? Helm is a client you run, not a control loop — it renders and applies once and doesn’t continuously watch, so nothing re-asserts desired state when the cluster drifts. For that you pair it with GitOps (Argo CD) reconciling against Git.
  • What breaks if I remove it? Multi-environment deploys revert to copy-pasted YAML where one missed edit ships a bug, environments drift apart, and rollback becomes a frantic manual re-apply of seven files in the right order under pressure — instead of one versioned, atomic command.
  1. What is the “too many YAML files” problem, and why does copying manifests per environment cause drift over time?
  2. Map Helm’s vocabulary onto apt: what are a chart, a release, and a chart repository the equivalent of?
  3. How do templates plus a values file let three environments share one source of truth? What ends up actually differing between them?
  4. Why is helm rollback safer than manually reverting and re-applying YAML? Connect it to fast, safe recovery.
  5. Using the book’s thread, explain what manual step Helm removes — and why Helm being a client, not a controller, leaves a gap that GitOps fills.
Show answers
  1. One app is seven-plus manifests, and running it across dev/staging/prod the naive way means copying all of them per environment and hand-editing the few differences. That drifts because a change (say a container port) must be edited in every copy without missing one, and a fix applied in one environment but forgotten in another silently pulls them apart.
  2. A chart is the package, a release is the installed program (a named, versioned instance running in the cluster), and a chart repository is the package repository — mirroring apt’s package, installed program, and package repo.
  3. The manifests become templates with {{ ... }} placeholders filled from a values file, so you write the structure once and supply per-environment differences as data. Each environment is then one chart plus a small values file, and what differs is only the intended bits — replica counts, image tags, hostnames.
  4. Because a release is versioned, helm rollback snaps the entire app — all its objects — back to a known-good revision as one atomic transaction, versus hand-reverting each of seven files and re-applying in the right order under pressure. That’s the fast, low-risk recovery the DORA metrics reward.
  5. Helm removes copy-pasted, drift-prone multi-environment YAML and white-knuckle manual rollback, collapsing it into one templated chart plus values and a one-command versioned rollback. But Helm is a client you run — it renders and applies manifests once, it doesn’t continuously watch them — so nothing re-asserts the desired state if the cluster drifts. GitOps (e.g. Argo CD) fills that gap by continuously reconciling the cluster to match Git.