Why Helm Exists, and What a Release Is

The three jobs Helm actually does, why Helm 3 removed the in-cluster server, and how a release revision lives in a Secret you can inspect with kubectl.

beginner 18 min lesson hands-on task included

Helm is described as “the package manager for Kubernetes”, which is accurate and undersells the part that matters most in production. Templating is the feature people notice; release management is the feature they depend on.


Topic 1: The Problem Helm Solves

RAW MANIFESTS, COPIED PER ENVIRONMENT k8s/dev/ deployment.yamlservice.yamlingress.yamlconfigmap.yamlhpa.yaml k8s/staging/ deployment.yamlservice.yamlingress.yamlconfigmap.yamlhpa.yaml k8s/prod/ deployment.yamlservice.yamlingress.yamlconfigmap.yamlhpa.yaml Fifteen files. One image tag change touches three of them. Nobody knows which environment drifted, or when. ONE CHART, THREE VALUES FILES chart/templates/ deployment.yamlservice.yamlingress.yamlconfigmap.yamlhpa.yaml values-dev.yaml values-stg.yaml values-prod.yaml The templates are the contract; the values are the difference. One place to change behaviour, three declared configurations. HELM IS THREE THINGS, AND THE THIRD IS THE ONE PEOPLE UNDERVALUE 1. A TEMPLATE ENGINE — one set of manifests, parameterised. 2. A PACKAGE FORMAT — a versioned, shareable artifact with dependencies. 3. A RELEASE MANAGER — it remembers what it installed, so upgrade, rollback and uninstall are possible at all.
The left-hand pattern is what every team does first, and it works until the third environment. The three numbered lines at the bottom are the actual job description — and the third one is why you cannot replace Helm with a shell script that runs envsubst.

A single service needs a Deployment, a Service, an Ingress, a ConfigMap, an HPA, a PDB and a ServiceAccount. Multiply by three environments and you have a directory tree where the same image tag appears in nine places, and no way to answer “what is different about staging” except by diffing directories.

Helm does three separate jobs:

  1. A template engine. One set of manifests, parameterised by values.
  2. A package format. A versioned, shareable .tgz with declared dependencies — so “install Postgres” is one line rather than a manifest hunt.
  3. A release manager. Helm records what it installed, which is what makes upgrade, rollback, uninstall and diff possible at all.

The third job is the one you cannot get from envsubst, sed, or kubectl apply -k. Without stored state, “undo the last deploy” means reconstructing what the previous manifests were.


Topic 2: Helm 3 Has No Server

HELM 3 IS A CLIENT. THERE IS NO SERVER COMPONENT. chart templates + defaults your values -f prod.yaml --set … helm (your laptop) renders locally kube-apiserver your kubeconfig, your RBAC THE RELEASE LIVES IN THE CLUSTER, AS A SECRET $ kubectl get secret -n prod -l owner=helm sh.helm.release.v1.checkout.v1 helm.sh/release.v1 1 sh.helm.release.v1.checkout.v2 helm.sh/release.v1 1 gzipped chart + values + rendered manifest, one Secret per revision. NO TILLER Helm 2 ran a pod with cluster-admin. Helm 3 removed it entirely. Old tutorials still say "helm init". Ignore them. WHAT THAT MEANS FOR PERMISSIONS AND FOR DEBUGGING Helm can do exactly what your kubeconfig can do — no more. And every question about a release is answerable with kubectl. A RELEASE IS SCOPED TO A NAMESPACE, NOT TO A CLUSTER The same chart can be installed many times under different release names — which is why every resource name should include .Release.Name.
Everything happens on your machine, with your kubeconfig. The only cluster-side artifact is a Secret per revision — which is why every question about a release is answerable with kubectl.

Helm 2 ran an in-cluster component called Tiller with, in most installations, cluster-admin. Helm 3 removed it entirely. The consequences:

  • Helm can do exactly what your kubeconfig can do. No privilege escalation path through a shared server, and RBAC applies to you personally.
  • Rendering is local. helm template produces the same YAML whether or not a cluster exists.
  • Release state lives in the cluster as Secrets, in the release’s namespace, one per revision.
kubectl get secret -n prod -l owner=helm
# sh.helm.release.v1.checkout.v1   helm.sh/release.v1   1   3d
# sh.helm.release.v1.checkout.v2   helm.sh/release.v1   1   2h

Each Secret holds the chart, the supplied values and the rendered manifest, gzipped and base64-encoded twice. You can read one by hand:

kubectl get secret sh.helm.release.v1.checkout.v2 -n prod \
  -o jsonpath='{.data.release}' | base64 -d | base64 -d | gunzip | head -40

Anything still telling you to run helm init or configure Tiller is describing Helm 2, which reached end of life in 2020. That includes a large fraction of the tutorials and course material still in circulation — worth knowing so you can date a document in one glance.

Two practical implications of storing state in Secrets: a release is scoped to a namespace, not a cluster (so the same chart can be installed many times under different names), and a large chart with many revisions consumes etcd space — which is what --history-max exists to bound.


Topic 3: The Vocabulary, Precisely

Four words that get used interchangeably and should not be:

TermWhat it is
ChartThe package: templates, default values, metadata. A directory or a .tgz.
ValuesThe configuration supplied at install time, merged over the chart’s defaults.
ReleaseOne installation of a chart into a namespace, under a name.
RevisionOne version of a release. Every install, upgrade and rollback creates a new one.
helm install checkout ./chart -n prod        # release "checkout", revision 1
helm upgrade checkout ./chart -n prod        # revision 2
helm install checkout-canary ./chart -n prod # a SECOND release of the same chart

That last line is the one worth internalising: a chart can be installed many times in one namespace. It is why every resource name in a well-written chart is derived from .Release.Name, and why a chart with hardcoded resource names fails the second time anyone tries to use it.


Topic 4: The Commands That Cover 90% of Use

# Find and inspect before installing
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo postgres
helm show values bitnami/postgresql | head -50    # the interface you get to configure
helm show chart bitnami/postgresql

# Install, upgrade, inspect
helm install checkout ./chart -n prod --create-namespace
helm upgrade --install checkout ./chart -n prod -f values-prod.yaml
helm list -n prod
helm status checkout -n prod
helm history checkout -n prod

# What did I actually deploy?
helm get values checkout -n prod            # values YOU supplied
helm get values checkout -n prod -a         # ...merged with the chart defaults
helm get manifest checkout -n prod          # the rendered YAML, as applied
helm get hooks checkout -n prod
helm get notes checkout -n prod

# Undo
helm rollback checkout 2 -n prod
helm uninstall checkout -n prod

helm upgrade --install is the form to use in automation: it installs if the release is absent and upgrades if it is present, so the pipeline needs no branching and re-running it is harmless.

helm show values before installing a third-party chart is the habit that separates people who configure charts from people who copy someone’s values file from a blog post. It is the chart’s public interface, and it is the documentation.


Topic 5: What Helm Is Not

Three misconceptions worth clearing early, because each one leads to a different kind of disappointment:

Helm is not a controller. It does not reconcile continuously. After helm upgrade returns, Helm stops caring; if someone edits a Deployment by hand, nothing corrects it until the next upgrade. Continuous reconciliation is what Argo CD and Flux add — covered in the delivery lesson.

Helm does not validate your Kubernetes objects. It renders text. A misspelled field renders happily and is rejected by the API server, which is why --dry-run=server exists and why client-side rendering alone proves less than it appears to.

Helm is not the only option, and not always the right one. Kustomize does overlays with no templating language and no release state; plain manifests are fine for one environment; an operator is the answer when the thing needs ongoing reconciliation logic. The Kubernetes path’s Helm and Kustomize lesson compares them directly rather than duplicating the argument here.

The rough decision: Helm when you are distributing something for others to install, or when you need release history and rollback. Kustomize when you own the manifests and want overlays without a template language. Many teams use both — Helm for third-party dependencies, Kustomize for their own workloads.


Topic 6: Installing Helm and Making It Pleasant

brew install helm                     # macOS
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

helm version
helm env                              # where Helm keeps its cache and config

Two plugins that most teams end up installing, and one setting:

helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade checkout ./chart -f values-prod.yaml    # see the change first

helm plugin install https://github.com/jkroepke/helm-secrets

helm completion zsh > "${fpath[1]}/_helm"   # tab-complete release names

helm-diff is the one to install today. Running an upgrade without seeing the diff is the Helm equivalent of terraform apply without reading the plan, and the plugin turns that into one command.

Try it yourself: install a chart, then answer three questions using only kubectl: which revisions exist, what manifest revision 1 contained, and which values were supplied. Doing it once without Helm’s own commands makes the storage model concrete — and it is exactly what you will fall back on when a release is stuck and helm itself is unhappy.

Common mistake: treating helm install as a one-way operation and reaching for kubectl edit when something needs changing. The edit works, the next helm upgrade silently reverts it, and the person who made the edit concludes that Helm is unreliable. Every change belongs in the chart or in the values — the three-way merge lesson explains exactly why the edit disappears.