Kubernetes Course
Helm Introduction
As your cluster grows, managing dozens of related Kubernetes manifests by hand becomes brittle. Helm is the Kubernetes package manager — it bundles related manifests into a versioned chart, parameterises them with a values file, and tracks every release so you can upgrade, rollback, and audit history atomically. This lesson covers the fundamentals: finding charts, installing them, customising with values, and managing the release lifecycle.
What Problem Does Helm Solve?
Installing NGINX Ingress Controller from scratch requires: a Namespace, ServiceAccount, two ClusterRoles, two ClusterRoleBindings, a ConfigMap, a Deployment, a Service, a ValidatingWebhookConfiguration, and an IngressClass. That is ten manifests, carefully ordered, with values that must be consistent across all of them. Helm packages all of this as a single installable unit — one command, one version, one rollback.
Without Helm
10 separate kubectl apply commands. Manual version tracking. Rollback means reverting each manifest individually. "What's deployed?" requires diffing YAML files.
With Helm
helm install ingress-nginx ingress-nginx/ingress-nginx. One command installs everything. helm rollback reverts all 10 resources atomically.
Core Concepts
| Term | What it means |
|---|---|
| Chart | A packaged collection of Kubernetes manifests plus a values.yaml. Versioned and distributable. Like an apt package for Kubernetes. |
| Release | An installed instance of a chart. One chart can produce many releases — mysql-payments and mysql-analytics from the same chart, different configurations. |
| Repository | An HTTP server hosting chart packages and an index. Add once with helm repo add, then install any chart from it by name. |
| Values | Configuration that customises the chart at install time — image tags, replica counts, resource limits. Supplied via -f values.yaml or --set key=value. |
| Revision | Every install or upgrade increments a counter. Helm stores rendered manifests for each revision, enabling rollback to any previous state. |
Finding and Installing Charts
The scenario: Your cluster needs NGINX Ingress Controller. Rather than hunting down upstream manifests, you find the official Helm chart, inspect its default values, and install it with settings tuned for your environment.
# Add repositories
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update # Refresh all repo indexes
# Search for charts
helm search repo ingress-nginx # Search added repos
helm search hub nginx # Search Artifact Hub (public registry)
# Inspect the chart before installing
helm show chart ingress-nginx/ingress-nginx # Metadata: description, version, dependencies
helm show values ingress-nginx/ingress-nginx # All configurable values with comments
# Tip: pipe to a file, edit, then install with -f:
helm show values ingress-nginx/ingress-nginx > my-values.yaml
# Install
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--version 4.9.1 \
--set controller.replicaCount=2 \
--set controller.service.type=LoadBalancer \
--wait \ # Block until all Pods are Ready
--timeout 5m
$ helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx --create-namespace \ --version 4.9.1 --set controller.replicaCount=2 --wait NAME: ingress-nginx LAST DEPLOYED: Mon Mar 10 11:04:22 2025 NAMESPACE: ingress-nginx STATUS: deployed REVISION: 1 $ helm list -n ingress-nginx NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION ingress-nginx ingress-nginx 1 2025-03-10 11:04:22 deployed ingress-nginx-4.9.1 1.10.0 # Helm stores release state as a Secret in the namespace $ kubectl get secrets -n ingress-nginx | grep helm sh.helm.release.v1.ingress-nginx.v1 helm.sh/release.v1 1 5m # This Secret contains the rendered manifests -- used for diffs and rollbacks
What just happened?
Helm rendered and applied all 10 resources in one command — Under the hood, Helm merged your --set overrides with the chart's default values, ran the Go templates to produce Kubernetes manifests, and applied them. The --wait flag blocked until every Deployment's Pods were Ready — not just submitted to the API server.
Release state lives in a namespace Secret — Helm 3 stores each revision's rendered manifests as a compressed Secret (type helm.sh/release.v1) in the release namespace. This is how helm rollback works without needing Git access — it reads the stored manifests from the target revision and re-applies them.
Customising with Values Files
For anything beyond a quick test, supply values in a YAML file rather than chaining --set flags. Values files are readable, committable to Git, and reusable across environments.
# ingress-nginx-values.yaml
controller:
replicaCount: 2
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
metrics:
enabled: true
serviceMonitor:
enabled: true # Create a ServiceMonitor for Prometheus
config:
use-forwarded-headers: "true"
ssl-protocols: "TLSv1.2 TLSv1.3"
podDisruptionBudget:
enabled: true
minAvailable: 1
# This is a values file -- applied at install/upgrade time with -f
# No direct kubectl output, but after helm install -f ingress-nginx-values.yaml:
$ helm get values ingress-nginx -n ingress-nginx
controller:
config:
ssl-protocols: TLSv1.2 TLSv1.3
use-forwarded-headers: "true"
metrics:
enabled: true
serviceMonitor:
enabled: true
replicaCount: 2
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
service:
annotations:
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-type: nlb
type: LoadBalancer
podDisruptionBudget:
enabled: true
minAvailable: 1# Install with a values file
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--version 4.9.1 \
-f ingress-nginx-values.yaml
# Upgrade with updated values (e.g. bumping replicaCount to 3)
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--version 4.9.1 \
-f ingress-nginx-values.yaml \
--atomic # Rollback automatically if upgrade fails
--timeout 5m
# Idempotent: install if not present, upgrade if exists
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
-f ingress-nginx-values.yaml
# Inspect what values are currently active for a release
helm get values ingress-nginx -n ingress-nginx # User-supplied values only
helm get values ingress-nginx -n ingress-nginx --all # Including chart defaults
$ helm upgrade ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx --version 4.9.1 \ -f ingress-nginx-values.yaml --atomic Release "ingress-nginx" has been upgraded. Happy Helming! REVISION: 2 $ helm history ingress-nginx -n ingress-nginx REVISION UPDATED STATUS CHART DESCRIPTION 1 2025-03-10 11:04:22 superseded ingress-nginx-4.9.1 Install complete 2 2025-03-10 11:22:47 deployed ingress-nginx-4.9.1 Upgrade complete
Rollback
If an upgrade causes problems, rolling back is a single command. Helm re-applies the rendered manifests from the target revision — no manual YAML editing.
# Roll back to revision 1
helm rollback ingress-nginx 1 -n ingress-nginx --wait
# After rollback -- history shows a new revision (history is append-only)
helm history ingress-nginx -n ingress-nginx
# REVISION STATUS DESCRIPTION
# 1 superseded Install complete
# 2 superseded Upgrade complete
# 3 deployed Rollback to 1 ← rollback creates revision 3, not a rewind
# See exactly what changed between revisions
helm diff revision ingress-nginx 1 2 -n ingress-nginx # requires helm-diff plugin
# Uninstall (removes all resources AND history)
helm uninstall ingress-nginx -n ingress-nginx
helm uninstall ingress-nginx -n ingress-nginx --keep-history # Keep history for audit
$ helm rollback ingress-nginx 1 -n ingress-nginx --wait Rollback was a success! Happy Helming! $ helm history ingress-nginx -n ingress-nginx REVISION UPDATED STATUS CHART DESCRIPTION 1 2025-03-10 11:04:22 superseded ingress-nginx-4.9.1 Install complete 2 2025-03-10 11:22:47 superseded ingress-nginx-4.9.1 Upgrade complete 3 2025-03-10 11:35:01 deployed ingress-nginx-4.9.1 Rollback to 1 ✓ $ helm uninstall ingress-nginx -n ingress-nginx release "ingress-nginx" uninstalled # All 10 resources deleted atomically $ helm uninstall ingress-nginx -n ingress-nginx --keep-history release "ingress-nginx" uninstalled $ helm history ingress-nginx -n ingress-nginx REVISION STATUS DESCRIPTION 1 superseded Install complete 2 superseded Upgrade complete 3 uninstalled Uninstallation complete ← history preserved for audit ✓
Essential Helm Commands Reference
| Command | What it does |
|---|---|
| helm repo add / update | Register and refresh chart repositories |
| helm search repo / hub | Find charts in repos or on Artifact Hub |
| helm show values <chart> | Print all configurable values and defaults |
| helm install <release> <chart> | Install a chart as a named release |
| helm upgrade --install | Install if absent, upgrade if present (idempotent) |
| helm list [-n namespace] | List all releases in a namespace |
| helm history <release> | Show revision history of a release |
| helm get values <release> | Show values currently applied to a release |
| helm get manifest <release> | Show rendered Kubernetes manifests for a release |
| helm rollback <release> <rev> | Roll back to a specific revision |
| helm uninstall <release> | Delete all release resources and history |
| helm lint <chart-dir> | Validate chart structure and template syntax |
Teacher's Note: Helm 2 vs Helm 3
You may encounter references to Helm 2 in older documentation. Helm 3 (released 2019) made two important security improvements: it removed Tiller — an in-cluster server component that had broad cluster-admin permissions and was a significant attack surface. Helm 3 runs entirely client-side using your own kubeconfig credentials. It also moved release state from a ConfigMap in kube-system to Secrets in the release namespace, scoping it properly per namespace rather than cluster-wide.
If you see instructions that say "install Tiller first" — that is Helm 2. Stop and find a Helm 3 guide. Helm 2 reached end-of-life in November 2020. All current charts, documentation, and tooling assume Helm 3.
Practice Questions
1. Which Helm command is idempotent — installing a release if it doesn't exist and upgrading it if it does — making it safe to run repeatedly in CI/CD pipelines?
2. Before installing a third-party chart, you want to see all the configuration options it supports and their default values. Which command shows this?
3. Where does Helm 3 store the rendered manifests for each release revision, enabling rollback without Git access?
Quiz
1. What is the difference between a Helm chart and a Helm release?
2. You upgrade a release to revision 2 and it causes problems. You run helm rollback myapp 1. What does helm history myapp show afterwards?
3. What does the --atomic flag do during a helm upgrade?
Up Next · Lesson 55
Helm Charts
Now that you can use charts, this lesson covers writing them from scratch: chart structure, the Go template engine, named helpers in _helpers.tpl, feature flags for optional resources, and multi-environment values files.