Helm use-cases
A practical look at Helm, the Kubernetes package manager — what a chart is, how Go templating and Sprig drive value substitution, how releases and revisions work, and when to reach for Helm over Kustomize
Helm is the package manager for Kubernetes. Where kubectl apply deals in individual manifests, Helm deals in charts: versioned, parameterised bundles of resources that you can install, upgrade and roll back as a single unit. If you have ever copied a folder of YAML between environments and hand-edited the image tag, replica count and namespace each time, Helm is the tool that turns that ritual into a templated, versioned artefact.
It is worth being explicit that this post is about Helm 3. The architecture changed meaningfully from Helm 2: there is no longer a server-side Tiller component sitting in your cluster with broad permissions. Helm 3 is a client-only binary that talks to the Kubernetes API directly using your existing kubeconfig and RBAC, and it stores release state as Secrets in the release's namespace. That single change removed most of the security objections people had with early Helm.
What a chart actually is
A chart is just a directory with a specific layout. The minimum you need is a Chart.yaml, a values.yaml, and a templates/ directory:
mychart
├── Chart.yaml # name, version, appVersion, dependencies
├── values.yaml # default configuration
├── charts/ # vendored sub-chart dependencies
└── templates/
├── deployment.yaml
├── service.yaml
├── _helpers.tpl # reusable named templates
└── NOTES.txt # printed after installChart.yaml carries the metadata. The important fields are version (the chart's own SemVer, bumped when you change the templates) and appVersion (the version of the application the chart deploys, deliberately independent). values.yaml holds the defaults, and everything under templates/ is rendered against those values to produce the manifests Helm sends to the cluster.
Templating with Go templates and Sprig
The templates are Go text/templates with the Sprig function library bolted on, plus a handful of Helm-specific helpers. Values flow in through the .Values object, and you get .Release, .Chart and .Capabilities for context. A trimmed-down deployment template looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount | default 1 }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
{{- if .Values.resources }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- end }}The interesting parts are the bits that go beyond substitution. default and toYaml come from Sprig; include and nindent are how you compose and indent named templates defined in _helpers.tpl. The {{- and -}} trim markers chew up surrounding whitespace, which matters because the output has to be valid YAML. Conditionals (if/else), iteration (range) and the full Sprig toolkit are all available, and this is exactly the power that makes Helm capable of expressing complex, optional configuration. It is also the power that makes a sprawling chart hard to read.
Releases and revisions
When you install a chart you create a release, a named instance of that chart running in the cluster. Each install or upgrade produces a new revision, and Helm keeps the history. This is the feature that earns Helm its keep: deployments become reversible.
# install a release named "web" from a local chart
helm install web ./mychart --namespace prod --create-namespace
# render the manifests without touching the cluster (great in CI)
helm template web ./mychart -f values.prod.yaml
# upgrade with new values, installing if it doesn't exist yet
helm upgrade --install web ./mychart -f values.prod.yaml
# inspect history, then roll back to a known-good revision
helm history web
helm rollback web 3helm template is the workhorse for CI pipelines and for diffing what a change will produce before it lands. helm upgrade --install is the idempotent form most people wire into automation. And helm rollback is the reason you can reach for Helm during an incident instead of frantically reconstructing the previous state by hand.
Repositories
Charts are shared through repositories. These days that most commonly means an OCI registry, the same place your container images live, though the classic HTTP index.yaml repositories are still everywhere:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo bitnami/redis
helm install cache bitnami/redis --version 19.0.0This versioning-and-distribution story is genuinely Helm's strongest argument. Pulling a well-maintained, pinned chart for something like Redis, Prometheus or an ingress controller is a far better experience than vendoring and maintaining a pile of upstream YAML yourself.
Helm or Kustomize?
The two tools solve overlapping problems with opposite philosophies. Kustomize is template-free: you start from concrete, valid manifests and layer patches over them per environment. There are no {{ }} braces and the output is always real Kubernetes YAML, which keeps things legible and easy to reason about.
Helm leans the other way: it asks you to author logic up front so a single chart can stretch across many situations through values and conditionals. I tend to reach for Kustomize when I own the manifests and the variation between environments is small and structural: replica counts, resource limits, image tags. I reach for Helm when I am packaging something for other people to consume, when the variation is large or optional, or when I want first-class releases, revision history and rollback. They are not mutually exclusive either. Running Helm to render a chart and then post-processing the output with Kustomize is a perfectly reasonable pattern, and one I have used to bend an upstream chart to fit without forking it.
Try it in the lab
All effects →Band Structure
physicsNearly-free electron E-k diagram with Brillouin zone gaps.
condensed mattersolid stateTransmission Line Pulse
engineeringTDR — a voltage pulse travels, reflects, and inverts on a mismatched line.
rftdrimpedanceWave Superposition
physicsInterference of two plane waves — beats, standing waves, and nodes.
wavesinterference
More from the blog
Kubernetes-centric Continuous Delivery - Part 2 (Tekton Pipelines)
An overview of the current kubernetes-native ci/cd ecosystem, With a demonstration of an end-to-end Tekton based workflow for build, test and deployment of a fullstack application
Kubernetes-centric Continuous Delivery - Part 1 (Developer Experience)
How we can get closer to unifying the local developer experience with production-grade kubernetes run-time environments
Getting started with Tekton
Developing basic CI and CD pipelines in Tekton and deploying this both to local and cloud based environments