Think of Helm like the App Store on your phone 📱. When you want Instagram, you don't write the app yourself — you just search, tap install, and it's done. Helm does exactly that for Kubernetes apps. Instead of writing dozens of messy YAML files by hand, you install a ready-made package called a Chart with one command.
What is Helm?
Helm is the official package manager for Kubernetes. It was adopted by the Cloud Native Computing Foundation (CNCF) in 2018 and is used by millions of engineers worldwide.
Without Helm, deploying a single app (like a database) to Kubernetes means writing and managing many YAML files separately — one for Deployment, one for Service, one for ConfigMap, one for Secret, and so on. One small typo can cause a production outage.
💡 Think of it like: Building IKEA furniture. Without Helm, someone threw all the pieces on the floor with no instructions. With Helm, you get a neat instruction booklet — and the whole wardrobe assembles in minutes!
- Chart → The package (like an app installer)
- Release → One installed copy of a chart running in your cluster
- Repository → A store where charts are published and shared
- Values → The settings you tweak to customise the chart for your needs
- Revision → Every upgrade or rollback creates a new revision number
Why Helm? The Problem It Solves
Imagine you need to deploy your AI weather model to three environments — dev, staging, and production. Without Helm you need three near-identical sets of YAML files, each with slightly different values like replica count, image tag, and resource limits.
That means 30+ files to maintain. Change the app port? You update 30 files. Forget one? Production breaks.
With Helm you have one chart and three tiny values files. Change the port in one place and all three environments stay in sync.
Without Helm: With Helm:
deployment-dev.yaml chart/
deployment-staging.yaml templates/
deployment-prod.yaml deployment.yaml ← one template
service-dev.yaml service.yaml ← reused
service-staging.yaml configmap.yaml ← everywhere
service-prod.yaml values-dev.yaml ← small override
configmap-dev.yaml values-staging.yaml ← small override
configmap-staging.yaml values-prod.yaml ← small override
configmap-prod.yaml
(30+ files, easy to break!) (clean, reusable, maintainable!)
See the difference? Helm turns chaos into order. 🎯
Update — Helm 4 is Here!
Helm 4 was released at KubeCon North America in November 2025. It is the biggest update in six years and brings three major improvements every MLOps engineer needs to know about.
- WebAssembly (Wasm) Plugins → Plugins now run in a secure sandbox. No more risk of a rogue plugin taking over your cluster.
- Server-Side Apply (SSA) → Helm 4 uses the Kubernetes API server to apply changes. This prevents conflicts when multiple tools (like ArgoCD + Helm) manage the same resources.
- OCI Registry as the new default → Charts are now stored and shared using the same registries as your Docker images. No more separate chart servers needed.
⚠️ Important: Helm 3 security fixes end on November 11, 2026. Plan your migration to Helm 4 before that date to stay safe and supported.
Installing Helm
Let's get Helm installed first. It takes less than two minutes!
Step 1: Install Helm on Mac
# Using Homebrew (recommended)
brew install helm
# Verify it installed correctly
helm version
Step 2: Install Helm on Linux
# Download and run the official install script
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Verify it installed correctly
helm version
Step 3: Install Helm on Windows
# Using Chocolatey
choco install kubernetes-helm
# Or using winget
winget install Helm.Helm
# Verify it installed correctly
helm version
Expected output after any install:
version.BuildInfo{Version:"v4.0.0", GitCommit:"abc123", GoVersion:"go1.22.0"}
You are ready to use Helm! 🎉
Your First Helm Chart — Install nginx in 60 Seconds
Let's install a real web server (nginx) on your cluster using Helm. This is the fastest way to see Helm working with your own eyes!
Step 1: Add a Chart Repository
# Add the Bitnami chart repository (one of the most popular)
helm repo add bitnami https://charts.bitnami.com/bitnami
# Always update after adding a new repo
helm repo update
Expected output:
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
Step 2: Search for a Chart
# Search in your added repositories
helm search repo nginx
# Search the public Artifact Hub (thousands of charts)
helm search hub nginx
Expected output:
NAME CHART VERSION APP VERSION DESCRIPTION
bitnami/nginx 18.2.4 1.27.3 NGINX Open Source is a web server that can be...
Step 3: Install the Chart
# Install nginx — give your release a name and choose a namespace
helm install my-nginx bitnami/nginx \
--namespace web-apps \
--create-namespace
Expected output:
NAME: my-nginx
LAST DEPLOYED: Thu Mar 19 2026
NAMESPACE: web-apps
STATUS: deployed
REVISION: 1
NOTES:
Get the application URL by running these commands:
export SERVICE_IP=$(kubectl get svc --namespace web-apps my-nginx ...)
echo "Visit http://$SERVICE_IP to use your application"
Step 4: Verify It Is Running
# See all Helm releases in your cluster
helm list --all-namespaces
# See the Pods that Helm created
kubectl get pods -n web-apps
Helm created the Deployment, Service, ConfigMap, and all the other pieces automatically — with just one command! 🚀
Understanding Helm Chart Structure
Now let's look inside a Helm chart. Run this command to create a brand-new chart from scratch:
helm create weather-ai
Helm creates this folder structure for you:
weather-ai/
├── Chart.yaml ← metadata: chart name, version, description
├── values.yaml ← default settings your chart uses
├── charts/ ← dependent charts live here
├── templates/ ← Kubernetes YAML templates go here
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── _helpers.tpl ← reusable template snippets
│ └── NOTES.txt ← message printed after install
└── .helmignore ← files to skip when packaging
Think of it like a recipe book 📖. The Chart.yaml is the cover page. The values.yaml is the list of ingredients with defaults. The templates/ folder is the actual cooking instructions.
The Chart.yaml File — The Identity Card
Every chart must have a Chart.yaml file. It tells Helm what the chart is called, what version it is, and what app it deploys:
# Chart.yaml
apiVersion: v2
name: weather-ai
description: AI weather prediction model for Kubernetes
type: application
version: 1.0.0 # the chart version (you change this)
appVersion: "3.1.0" # the app version inside the chart
maintainers:
- name: ML Team
email: ml-team@company.com
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- version → The version of the chart itself — bump this every time you change the chart
- appVersion → The version of your actual application (like your Docker image tag)
- dependencies → Other charts this chart needs (like PostgreSQL for a database)
The values.yaml File — Your Settings Panel
This is where all the default settings for your chart live. Think of it like a control panel with switches and dials that are set to sensible defaults:
# values.yaml (default settings)
replicaCount: 1
image:
repository: weather-ai
pullPolicy: IfNotPresent
tag: "3.1.0"
service:
type: ClusterIP
port: 80
ingress:
enabled: false
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
# ML-specific settings
model:
version: "v3.1"
batchSize: 32
logLevel: "info"
These are the defaults. You override them per environment without changing this file. 🎯
A Template File — Where the Magic Happens
Templates are YAML files with special placeholders wrapped in {{ }} double curly braces. Helm fills in those placeholders using your values at install time:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-weather-ai
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Release.Name }}
version: {{ .Chart.AppVersion }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
spec:
containers:
- name: weather-ai
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.port }}
env:
- name: MODEL_VERSION
value: {{ .Values.model.version | quote }}
- name: BATCH_SIZE
value: {{ .Values.model.batchSize | quote }}
How the placeholders work:
{{ .Release.Name }}→ The name you gave when runninghelm install{{ .Values.replicaCount }}→ Reads fromvalues.yaml— default is 1{{ .Chart.AppVersion }}→ Reads fromChart.yaml{{ .Values.image.tag | quote }}→ Adds quotes around the value automatically
When you run helm install my-release ./weather-ai, Helm replaces all those {{ }} slots with real values and sends the final YAML to Kubernetes. Brilliant! 💡
Customising a Chart with Your Own Values
This is where Helm really shines. You can install the same chart multiple times with different settings — no duplication needed!
Method 1: Override Values Inline with --set
# Install with 3 replicas instead of the default 1
helm install weather-prod ./weather-ai \
--set replicaCount=3 \
--set image.tag=3.2.0 \
--set model.logLevel=warn \
--namespace production \
--create-namespace
Quick and great for one-off overrides. Not ideal for many values though.
Method 2: Use a Custom Values File (Best Practice!)
Create a separate values file per environment:
# values-production.yaml
replicaCount: 5
image:
tag: "3.2.0"
service:
type: LoadBalancer
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 1000m
memory: 2Gi
model:
logLevel: "warn"
batchSize: 128
# Install using the production values file
helm install weather-prod ./weather-ai \
--values values-production.yaml \
--namespace production \
--create-namespace
The rule is simple: values in your file override the defaults in values.yaml. Anything you don't mention keeps its default. Clean and predictable! ✅
Essential Helm Commands — Day-to-Day Usage
Installing and Upgrading
# Install a chart
helm install <release-name> <chart> -n <namespace>
# Upgrade an existing release
helm upgrade weather-prod ./weather-ai -f values-production.yaml -n production
# Install OR upgrade in one command (most common in CI/CD)
helm upgrade --install weather-prod ./weather-ai \
--values values-production.yaml \
--namespace production \
--create-namespace
Inspecting Releases
# List all releases
helm list --all-namespaces
# See detailed status of a release
helm status weather-prod -n production
# See all revisions (history) of a release
helm history weather-prod -n production
Expected output of helm history:
REVISION UPDATED STATUS CHART DESCRIPTION
1 Thu Mar 19 09:00 2026 superseded weather-ai-1.0.0 Install complete
2 Thu Mar 19 14:00 2026 superseded weather-ai-1.0.0 Upgrade complete
3 Thu Mar 19 16:00 2026 deployed weather-ai-1.1.0 Upgrade complete
Rolling Back — Your Safety Net
This is one of Helm's most powerful features. Something went wrong? Roll back to any previous revision instantly:
# Roll back to the previous revision
helm rollback weather-prod -n production
# Roll back to a specific revision number
helm rollback weather-prod 2 -n production
# Verify the rollback
helm history weather-prod -n production
Rollback is instant! Helm doesn't need to re-deploy anything — it just re-applies the previously stored revision. 🛡️
Previewing Before You Deploy
# Preview the final YAML that Helm would generate — nothing gets deployed
helm template weather-prod ./weather-ai -f values-production.yaml
# Check your chart for common mistakes
helm lint ./weather-ai
# Dry-run: simulate an install without actually doing it
helm upgrade --install weather-prod ./weather-ai \
--values values-production.yaml \
--dry-run \
--namespace production
Always run helm lint and helm template before deploying to production. It will catch errors before they cause outages. ✅
Uninstalling a Release
# Remove everything Helm created for this release
helm uninstall weather-prod -n production
# Verify it is gone
helm list -n production
Helm Dependencies — Charts Inside Charts
Your ML model needs a database. Your database needs a message queue. Helm lets you package all of these together as dependencies.
Think of it like ordering a combo meal 🍔🍟🥤. You order one thing and all three arrive together, perfectly sized for each other.
Step 1: Declare Dependencies in Chart.yaml
# Chart.yaml
dependencies:
- name: postgresql
version: "12.1.5"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled # only install if enabled in values
- name: redis
version: "17.3.7"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
Step 2: Control Dependencies in values.yaml
# values.yaml
postgresql:
enabled: true
auth:
postgresPassword: "" # leave blank, pass via Secret
primary:
persistence:
size: 20Gi
redis:
enabled: false # turn off for dev, on for prod
Step 3: Download and Install Dependencies
# Download all declared dependencies into charts/ folder
helm dependency update ./weather-ai
# Now install — Helm deploys everything together
helm install weather-stack ./weather-ai \
--values values-production.yaml \
--namespace ml-team \
--create-namespace
One command deploys your ML model, its database, and its cache all together in the right order. 🎉
Helm Hooks — Running Tasks at the Right Moment
Sometimes you need to run a task before or after a deployment. For example, run database migrations before your app starts, or send a Slack notification after a successful deploy.
Think of hooks like alarm clocks ⏰ set at specific moments during the install lifecycle.
# templates/db-migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-db-migration
annotations:
"helm.sh/hook": pre-upgrade # run before upgrade starts
"helm.sh/hook-weight": "-5" # run earlier than other hooks
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
spec:
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["python", "manage.py", "migrate"]
restartPolicy: Never
Common hook types:
pre-install→ Runs before any resources are createdpost-install→ Runs after all resources are createdpre-upgrade→ Runs before an upgrade starts — perfect for DB migrationspost-upgrade→ Runs after upgrade completes — great for smoke testspre-rollback→ Runs before a rollback beginspost-rollback→ Runs after rollback completes
OCI Registries — Way to Share Charts
Until recently, Helm charts were stored on special HTTP servers called chart repositories, OCI registries are the new standard. Your charts now live alongside your Docker images in the same registry — no separate server needed!
Think of it like keeping your app's recipe (chart) and ingredients (image) on the same shelf in the same store. Makes total sense! 🗄️
Push a Chart to an OCI Registry
# Step 1: Log in to your registry
helm registry login ghcr.io
# Step 2: Package your chart into a .tgz file
helm package ./weather-ai
# Output: weather-ai-1.0.0.tgz
# Step 3: Push to OCI registry (GitHub Container Registry in this example)
helm push weather-ai-1.0.0.tgz oci://ghcr.io/my-org/charts
Install a Chart from an OCI Registry
# Install directly from OCI registry — no repo add needed!
helm install weather-prod \
oci://ghcr.io/my-org/charts/weather-ai \
--version 1.0.0 \
--namespace production
# Helm 4: Install by digest for maximum security (supply chain pinning)
helm install weather-prod \
oci://ghcr.io/my-org/charts/weather-ai@sha256:abc123def456 \
--namespace production
Installing by digest means even if someone tampers with the tag in the registry, your install will fail because the cryptographic hash won't match. This is the gold standard for production security ! 🔒
Helmfile — Managing Multiple Releases Declaratively
When your cluster grows and you have 10, 20, or 50 Helm releases to manage, running individual helm upgrade commands becomes painful. Helmfile is the solution.
Think of Helmfile like a grocery list 📝. Instead of going to the store and picking things up one by one, you hand the whole list to Helmfile and say "make it happen".
# helmfile.yaml
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
releases:
- name: weather-model
namespace: ml-team
chart: ./weather-ai
values:
- values-production.yaml
- name: postgres
namespace: ml-team
chart: bitnami/postgresql
version: "12.1.5"
set:
- name: primary.persistence.size
value: 50Gi
- name: nginx-ingress
namespace: ingress
chart: bitnami/nginx-ingress-controller
version: "9.x.x"
# Install or upgrade all releases in one command
helmfile apply
# Preview what would change — no actual deploy
helmfile diff
# Destroy all releases
helmfile destroy
One file. One command. Your entire cluster's desired state applied consistently.
Testing Your Chart
Helm has a built-in test framework. You write a test Pod that verifies your app is working correctly after install. Helm runs it and reports pass or fail.
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-test-connection"
annotations:
"helm.sh/hook": test # marks this as a test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ .Release.Name }}-weather-ai:{{ .Values.service.port }}/health']
restartPolicy: Never
# Run all tests for a release
helm test weather-prod -n production
Expected output:
NAME: weather-prod
LAST DEPLOYED: Thu Mar 19 2026
NAMESPACE: production
STATUS: deployed
TEST SUITE: weather-prod-test-connection
Last Started: Thu Mar 19 16:45:00 2026
Last Completed: Thu Mar 19 16:45:03 2026
Phase: Succeeded
Green light! Your app passed its health check after deploying. 🟢
Real MLOps Example — Deploying a Complete ML Platform
Let's put everything together. Here is how a real ML team deploys a complete weather prediction platform using Helm.
Step 1: Create the Chart
helm create weather-ai
Step 2: Write values per environment
# values-dev.yaml
replicaCount: 1
image:
tag: "latest"
model:
logLevel: "debug"
batchSize: 8
postgresql:
enabled: true
auth:
postgresPassword: "dev-password"
# values-production.yaml
replicaCount: 5
image:
tag: "3.2.0"
model:
logLevel: "warn"
batchSize: 128
service:
type: LoadBalancer
postgresql:
enabled: true
primary:
persistence:
size: 100Gi
Step 3: Lint and Preview
# Check the chart for mistakes
helm lint ./weather-ai
# Preview what gets deployed to production
helm template weather-prod ./weather-ai -f values-production.yaml
Step 4: Deploy to Each Environment
# Deploy to dev
helm upgrade --install weather-dev ./weather-ai \
--values values-dev.yaml \
--namespace dev \
--create-namespace
# Deploy to production
helm upgrade --install weather-prod ./weather-ai \
--values values-production.yaml \
--namespace production \
--create-namespace \
--atomic # auto-rollback if anything fails!
The --atomic flag is critical for production. If any Pod fails to start, Helm automatically rolls back to the last working revision. Zero human intervention needed! 💎
Step 5: Package and Push to OCI Registry
# Package it
helm package ./weather-ai
# Push to your company's OCI registry
helm push weather-ai-1.0.0.tgz oci://ghcr.io/my-company/charts
# Anyone on the team can now install it from anywhere
helm install weather-prod \
oci://ghcr.io/my-company/charts/weather-ai \
--version 1.0.0 \
--values values-production.yaml \
--namespace production
Quick Summary 📝
What we learned :
- What Helm is → The package manager for Kubernetes. Install complex apps with one command.
- Key concepts → Chart (package), Release (installed instance), Values (your settings), Revision (every change)
- Chart structure →
Chart.yaml(identity),values.yaml(defaults),templates/(YAML with placeholders) - Installing →
helm install,helm upgrade --install, customise with--valuesor--set - Rollback →
helm rollbackreverts to any previous revision instantly - Dependencies → Declare sub-charts in
Chart.yaml, download withhelm dependency update - Hooks → Run Jobs before or after deploy — great for DB migrations and smoke tests
- OCI Registries → The standard — store and share charts alongside Docker images
- Helmfile → Manage dozens of releases declaratively from one YAML file
- Helm 4 → WebAssembly plugins, Server-Side Apply, OCI by default
The Ultimate Cheat Sheet 📋
# ── REPOS ─────────────────────────────────────────────
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo nginx
# ── INSTALL / UPGRADE ─────────────────────────────────
helm install <name> <chart> -n <ns> --create-namespace
helm upgrade --install <name> <chart> -f values.yaml -n <ns>
helm upgrade --install <name> <chart> --atomic -n <ns>
# ── INSPECT ───────────────────────────────────────────
helm list --all-namespaces
helm status <name> -n <ns>
helm history <name> -n <ns>
helm get values <name> -n <ns>
# ── ROLLBACK ──────────────────────────────────────────
helm rollback <name> -n <ns> # previous revision
helm rollback <name> 2 -n <ns> # specific revision
# ── DRY RUN / PREVIEW ─────────────────────────────────
helm template <name> <chart> -f values.yaml
helm lint <chart-dir>
helm upgrade --install <name> <chart> --dry-run
# ── DEPENDENCIES ──────────────────────────────────────
helm dependency update <chart-dir>
helm dependency list <chart-dir>
# ── OCI REGISTRY ──────────────────────────────────────
helm registry login ghcr.io
helm package <chart-dir>
helm push <chart.tgz> oci://registry/org/charts
helm install <name> oci://registry/org/charts/<chart> --version x.x.x
# ── CLEANUP ───────────────────────────────────────────
helm uninstall <name> -n <ns>
Happy Helming! ⛵✨
Comments
Post a Comment