Mark Ku's Blog
Podcast ConversationAI dialogue version of this article · Mandarin audio
Audio for this article is powered by VoAIVoAI

Preface

The evolution of containerization has taken us from Docker to Kubernetes:

Docker era:

  • Gentle learning curve — one docker run boots an app
  • Single container, simple config, easy to grasp
  • Great for small projects and personal dev

Kubernetes era:

  • Powerful, but a steep learning curve
  • Need to manage many resources (Pod, Service, Deployment, Ingress, etc.)
  • File counts explode, maintenance cost is high
  • But delivers enterprise-grade scalability, HA, and automation

Enter Helm:

  • Makes Kubernetes complexity manageable
  • Just as Docker made containers simple, Helm makes K8s deployment simple
  • One Chart can manage an entire app's lifecycle
  • Easy swapping, clear version management, easy rollback

But Helm brings new challenges too:

  • More configuration: on top of YAML, you also learn Chart structure, values.yaml, and template syntax
  • Steeper learning curve still: need to understand Go templates, Helm commands, the Chart ecosystem
  • Beginners may feel Helm is more complex than plain YAML
  • But once you grok it, maintenance and migration costs end up lower

What is Helm? Why do you need it?

Imagine deploying a complete web app on Kubernetes. You'd need:

  • Deployment (Pod management)
  • Service (network)
  • Ingress (external access)
  • ConfigMap (configuration)
  • Secret (passwords)

If you wrote these YAMLs manually for each environment (dev, staging, prod), you'd find:

Problem 1: file explosion

  • Dev: 5 YAML files
  • Staging: 5 YAML files
  • Prod: 5 YAML files
  • 15 files to maintain in total!

Problem 2: parameter chaos

  • Dev uses nginx:1.20, prod uses nginx:1.21
  • Dev has 1 replica, prod has 3
  • Every release means manually editing a pile of files

Problem 3: hard version management

  • When something breaks, you don't know which version to roll back to
  • No clear deployment history

How does Helm solve these?

Helm is the "package manager for Kubernetes" — it bundles multiple YAML files into a "Chart":

One Chart for all environments

  • Use values.yaml to control per-environment parameters
  • Dev: helm install my-app ./my-chart -f dev-values.yaml
  • Prod: helm install my-app ./my-chart -f prod-values.yaml

Versioning and rollback

  • Each deployment gets a revision (REVISION 1, 2, 3...)
  • When something breaks: helm rollback my-app 2 rolls back instantly

Rich ecosystem

  • No need to write Charts yourself — use ready-made ones (MySQL, Redis, Nginx, etc.)
  • Like a phone App Store, with thousands of apps

In short: Helm makes deploying apps to K8s as easy as installing apps on your phone!

How does Helm work?

1. A Chart is an "app bundle"

my-web-app/
├── Chart.yaml          # App info (name, version)
├── values.yaml         # Default settings
├── templates/          # YAML templates
│   ├── deployment.yaml
│   ├── service.yaml
│   └── ingress.yaml
└── charts/             # Other dependent apps

2. Template + values = final YAML

Template (templates/deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.app.name }}    # Replaced here
spec:
  replicas: {{ .Values.replicas }} # Replaced here
  template:
    spec:
      containers:
      - name: {{ .Values.app.name }}
        image: {{ .Values.image }}:{{ .Values.tag }}

Values (values.yaml):

app:
  name: "my-web-app"
replicas: 3
image: "nginx"
tag: "1.21"

Final YAML produced:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-web-app
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: my-web-app
        image: nginx:1.21

3. Different values files for different environments

Dev (dev-values.yaml):

replicas: 1
image: "nginx"
tag: "1.20"

Prod (prod-values.yaml):

replicas: 3
image: "nginx"
tag: "1.21"

Hands-on: deploy Elasticsearch in 5 minutes

Step 1: install Helm

# macOS
brew install helm

# Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Windows
choco install kubernetes-helm

Step 2: add Elastic's "app store"

helm repo add elastic https://helm.elastic.co
helm repo update

Step 3: one-command Elasticsearch install

helm install elasticsearch elastic/elasticsearch

That's it! Elasticsearch starts deploying.

Step 4: check deployment status

# List all installed apps
helm list

# Detailed status of Elasticsearch
helm status elasticsearch

# Deployment history
helm history elasticsearch

Step 5: upgrade or roll back

# Upgrade to a new version
helm upgrade elasticsearch elastic/elasticsearch --set imageTag=8.5.0

# If broken, roll back to previous
helm rollback elasticsearch

Helm vs raw YAML: side-by-side

Deploying Nginx with raw YAML

deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dev
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.20

service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-dev-service
spec:
  selector:
    app: nginx-dev
  ports:
  - port: 80

Prod requires another similar set of files...

Deploying Nginx with Helm

One command does it:

# Dev
helm install nginx bitnami/nginx --set replicaCount=1,imageTag=1.20

# Prod
helm install nginx bitnami/nginx --set replicaCount=3,imageTag=1.21

Comparison:

ItemRaw YAMLHelm
File countenvs × resources1 Chart
Parameter mgmtEdit each file by handUnified values.yaml
VersioningManual logsAuto version mgmt
RollbackManual rebuildhelm rollback
EcosystemWrite your ownThousands of ready Charts

Common Helm commands

# Basics
helm list                    # List installed apps
helm status <app-name>       # App status
helm history <app-name>      # Deployment history

# Install and upgrade
helm install <name> <chart>   # Install
helm upgrade <name> <chart>  # Upgrade
helm uninstall <name>        # Uninstall

# Rollback
helm rollback <name> <revision>  # Rollback to specific revision
helm rollback <name>             # Rollback to previous

# View and download
helm search repo <keyword>   # Search available Charts
helm pull <chart>            # Download Chart locally
helm template <chart>        # Preview generated YAML

How is Helm Rollback implemented?

A common question: how does helm rollback roll back so quickly? The mechanism isn't complex.

Release and Revision mechanism

Each helm install or helm upgrade creates a Release with a Revision number:

# View deployment history
helm history my-app

# Sample output:
REVISION    STATUS      CHART           DESCRIPTION
1           superseded  my-app-1.0.0    Install complete
2           superseded  my-app-1.0.1    Upgrade complete
3           deployed    my-app-1.0.2    Upgrade complete

Where is the historical data stored?

Helm stores the complete info for each Revision in Kubernetes Secrets:

# View Helm-created Secrets
kubectl get secrets -l owner=helm

# Sample output:
NAME                          TYPE                 DATA
sh.helm.release.v1.my-app.v1  helm.sh/release.v1   1
sh.helm.release.v1.my-app.v2  helm.sh/release.v1   1
sh.helm.release.v1.my-app.v3  helm.sh/release.v1   1

Each Secret contains:

  • Chart templates: all YAML templates used at the time
  • Values: settings used at the time
  • Manifest: final Kubernetes resource YAML produced
  • Metadata: revision number, status, timestamps, etc.

How Rollback works

When you run helm rollback my-app 2, Helm:

┌─────────────────────────────────────────────────────────┐
│  1. Read target revision                                  │
│     Pull data from Secret sh.helm.release.v1.my-app.v2   │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  2. Decompress and decode                                 │
│     Secret content is base64 + gzip compressed           │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  3. Get the old manifest                                  │
│     Obtain the complete Kubernetes YAML produced then     │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  4. Three-way merge                                       │
│     Compare: old manifest vs current state vs target     │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  5. Apply changes                                         │
│     kubectl apply updates resources to target state      │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  6. Create a new Revision                                 │
│     Rollback itself creates a new revision (e.g., v4)    │
└─────────────────────────────────────────────────────────┘

Real example: viewing Secret contents

# Get a specific Release version's data
kubectl get secret sh.helm.release.v1.my-app.v2 -o jsonpath='{.data.release}' | base64 -d | gunzip

# Outputs the complete Release info as JSON, including:
# - chart: complete Chart contents
# - config: values at the time
# - manifest: produced YAML
# - version: revision number

Why is Rollback so fast?

  1. No Chart re-download: all data is in the Secret
  2. No template re-render: the Manifest is pre-rendered
  3. Only diffed resources updated: three-way merge processes only what truly needs to change

Caveats

⚠️ Secret size limit: Kubernetes Secret defaults to 1MB max — large Charts may have issues

⚠️ Historical revisions accumulate: defaults to keeping 10 — adjust via --history-max:

helm upgrade my-app ./my-chart --history-max 5

⚠️ Rollback doesn't restore data: only restores Kubernetes resource configs — databases and other persistent data don't roll back

Difference from Kubernetes-native Rollback

ItemHelm Rollbackkubectl rollout undo
ScopeWhole Release (multiple resources)Single Deployment
History storageKubernetes SecretReplicaSet
Config restoreFull restore (incl. values)Only Pod template
ConfigMap/Secret✅ Restored❌ Not restored

Helm versions vs Git

A common question: "How do Helm Revisions relate to Git commits?"

Answer: they're entirely separate systems with no direct relationship.

┌─────────────────────────────────────────────────────────────────┐
│                  Two independent versioning systems              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Git (source code version control)  Helm (deploy versioning)   │
│   ├── commit abc123                  ├── Revision 1             │
│   ├── commit def456                  ├── Revision 2             │
│   ├── commit ghi789                  ├── Revision 3             │
│   └── stored in Git Repository       └── stored in K8s Secret   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

They track different things:

ItemGitHelm Revision
TracksSource code changesDeployment-state changes
StorageGit RepositoryKubernetes Secret
Version formatSHA hash (like abc123)Incrementing number (1, 2, 3...)
Rollback effectCode goes back in timeK8s resources go back in time

Why are they independent?

  1. Deployments don't always correspond to code changes

    • You might just change replicas: 3replicas: 5 (no code change)
    • Helm gets a new Revision; Git gets no new commit
  2. One commit may be deployed many times

    • The same code version may be deployed to dev, staging, prod
    • Each deployment is a new Helm Revision
  3. Deployments may fail and retry

    • Same commit may be deployed multiple times due to settings tweaks

How are they linked in practice?

Even though they're independent, in CI/CD we typically link them via Labels or Annotations:

# Add Git info in values.yaml or at deploy time
metadata:
  annotations:
    git.commit: "abc123def"
    git.branch: "main"
    git.repo: "github.com/myorg/myapp"

Inject Git info at deploy time:

# Auto-inject Git commit in CI/CD
helm upgrade my-app ./chart \
  --set gitCommit=$(git rev-parse --short HEAD) \
  --set gitBranch=$(git branch --show-current)

Benefits:

  • When something breaks, quickly trace which commit caused it
  • Easier to track "which code version this deployment corresponds to"
  • But Rollback still uses Helm Revision, not Git commit

Summary: By storing every revision's complete info in a Secret, Helm achieves fast and complete rollback. That's also why Helm is more reliable than hand-managing YAML.

Where to find ready-made Charts?

2. Bitnami Charts (high quality)

3. Official Charts

How to choose a good Chart?

  1. Maintenance status: updated within the last 6 months
  2. Download count: high downloads usually means more stable
  3. Documentation: complete usage instructions
  4. Community: active discussion on GitHub

Practical advice

1. Start simple

  • Try ready-made Charts first (Nginx, Redis)
  • Once familiar, consider building your own

2. Environment separation

  • Use different values.yaml for dev/staging/prod
  • Don't directly modify Chart source files

3. Version control

  • Put values.yaml under Git management
  • Record version numbers for each deployment

4. Back up important data

  • Databases and other critical services need regular backups
  • Helm only manages app deployment, not data backups

FAQ

Q: Will Helm affect existing Kubernetes resources? A: No. Helm is a management tool only — it won't affect existing resources.

Q: Can I use Helm and kubectl together? A: Yes. Helm ultimately interacts with Kubernetes via kubectl.

Q: What if a Chart has problems? A: Use helm rollback to roll back, or helm uninstall to remove and reinstall.

Q: Is building your own Chart complicated? A: It can feel complex at first, but once familiar it's much simpler than hand-writing YAML.

Summary

Helm makes Kubernetes app deployment:

  • Simpler: deploy complex apps with one command
  • Safer: version management and quick rollback
  • More efficient: reuse existing Charts — don't reinvent the wheel

If you're still hand-writing piles of YAML, give Helm a try — you'll discover a new world!

References

Author

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。Read More

Found this useful?

The author's free tools, daily podcasts and newsletter are all here.

Mark Ku · This article is licensed under CC BY 4.0. Credit the author and link back to the original when reusing it.

Comments

Subscribe to Newsletter

Subscribe to get new posts delivered instantly — never miss a tech share.

By submitting, you agree to receive emails. You can anytime.

Popular Posts

View all
Mark Ku
··602

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution
Mark Ku
··490

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.
Mark Ku
··333

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki
Mark Ku
··264

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning
Mark Ku
··221

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1
Mark Ku
··215

Setting Up Samba on Ubuntu to Share Folders with Windows 11

Setting Up Samba on Ubuntu to Share Folders with Windows 11
用 Helm 佈署應用:為什麼選 Helm 佈署Kubernetes 容器應用、怎麼裝、和純 YAML 的差異 - Mark Ku's Tech Notes