---
title: "Deploying Apps with Helm: Why Helm for Kubernetes Container Apps, How to Install, and How It Differs from Plain YAML"
description: "A practical look at Helm's value, installation, and how it differs from raw Deployment YAML — plus official/community Chart sources and download examples."
canonical_url: "https://blog.markkulab.net/en/post/helm-chart-deploy"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/en/author/mark-ku"
site: "Mark Ku's Tech Notes"
date_published: "2025-09-24 09:00:00 +0800"
category: "DevOps"
tags: ["kubernetes", "helm", "devops", "chart", "artifact hub", "bitnami", "ci/cd"]
language: "en"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "when reusing or quoting, credit the author and link back to the original"
---

# Deploying Apps with Helm: Why Helm for Kubernetes Container Apps, How to Install, and How It Differs from Plain YAML

## 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):**
```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):**
```yaml
app:
  name: "my-web-app"
replicas: 3
image: "nginx"
tag: "1.21"
```

**Final YAML produced:**
```yaml
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):**
```yaml
replicas: 1
image: "nginx"
tag: "1.20"
```

**Prod (prod-values.yaml):**
```yaml
replicas: 3
image: "nginx"
tag: "1.21"
```

## Hands-on: deploy Elasticsearch in 5 minutes

### Step 1: install Helm
```bash
# 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"
```bash
helm repo add elastic https://helm.elastic.co
helm repo update
```

### Step 3: one-command Elasticsearch install
```bash
helm install elasticsearch elastic/elasticsearch
```

That's it! Elasticsearch starts deploying.

### Step 4: check deployment status
```bash
# List all installed apps
helm list

# Detailed status of Elasticsearch
helm status elasticsearch

# Deployment history
helm history elasticsearch
```

### Step 5: upgrade or roll back
```bash
# 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:**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dev
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.20
```

**service.yaml:**
```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:**
```bash
# 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:**

| Item | Raw YAML | Helm |
|------|---------|------|
| File count | envs × resources | 1 Chart |
| Parameter mgmt | Edit each file by hand | Unified values.yaml |
| Versioning | Manual logs | Auto version mgmt |
| Rollback | Manual rebuild | `helm rollback` |
| Ecosystem | Write your own | Thousands of ready Charts |

## Common Helm commands

```bash
# 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:

```bash
# 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**:

```bash
# 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

```bash
# 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`:
```bash
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

| Item | Helm Rollback | kubectl rollout undo |
|------|---------------|----------------------|
| Scope | Whole Release (multiple resources) | Single Deployment |
| History storage | Kubernetes Secret | ReplicaSet |
| Config restore | Full 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:**

| Item | Git | Helm Revision |
|------|-----|---------------|
| Tracks | Source code changes | Deployment-state changes |
| Storage | Git Repository | Kubernetes Secret |
| Version format | SHA hash (like `abc123`) | Incrementing number (1, 2, 3...) |
| Rollback effect | Code goes back in time | K8s resources go back in time |

**Why are they independent?**

1. **Deployments don't always correspond to code changes**
   - You might just change `replicas: 3` → `replicas: 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**:

```yaml
# 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:**
```bash
# 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?

### 1. Artifact Hub (most recommended)
- URL: https://artifacthub.io/
- Like Helm's "App Store"
- Thousands of apps you can use directly

### 2. Bitnami Charts (high quality)
- URL: https://charts.bitnami.com/bitnami
- Maintained by VMware, stable quality
- Includes MySQL, Redis, Kafka, and other common services

### 3. Official Charts
- Elastic: https://helm.elastic.co
- Grafana: https://grafana.github.io/helm-charts
- Prometheus: https://prometheus-community.github.io/helm-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

- [Artifact Hub](https://artifacthub.io/) — Helm Chart search platform
- [Bitnami Charts](https://charts.bitnami.com/bitnami) — High-quality Chart collection
- [Elastic Helm Charts](https://helm.elastic.co) — Official Elastic Charts

---

## About this article and its author

Originally published on [Mark Ku's Tech Notes](https://blog.markkulab.net/en/post/helm-chart-deploy)

License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — when reusing or quoting, credit the author and link back to the original

### About the author

**[Mark Ku](https://blog.markkulab.net/en/author/mark-ku)** — Software Solution Provider

- 10+ years senior software engineer, now an AI Builder
- Focused on large-platform architecture — North-American e-commerce, AI SaaS subscription billing
- Combining AI Agents and automation to build evolvable product foundations

### Free tools built by the author

All of these are free to use:

- [Free PDF Sign Tool](https://blog.markkulab.net/en/tools/pdf-sign): Online PDF sign tool — draw, type, or upload a signature, then drag, resize, and download. Everything runs in your browser; nothing is uploaded.
- [VS Code Refactory](https://blog.markkulab.net/en/tools/refactory): Refactory is a VS Code refactoring extension: 34 actions plus a 37-rule code-smell inspection layer with a Code Health dashboard, across 18 languages, backed by 534 tests. It learns your repo's conventions: where interfaces live, where DI is registered, whether 'use client' belongs. It ranks files by git churn × complexity so you know what to fix first, and hands any smell to the Claude Code already on your machine. Free to use, and your source never leaves your computer.
- [DB-Kit Database Manager](https://blog.markkulab.net/en/tools/db-kit): DB-Kit is a lightweight, cross-platform database manager built with Tauri + Rust + React. Manage MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite, MongoDB, Redis, Kafka, Elasticsearch and RabbitMQ from one consistent interface: passwords encrypted in the OS keychain, SSH tunnels, full CRUD, a visual query builder, stacked multi-statement result sets, cross-connection data transfer and compare/sync, Excel / CSV import & export, visualized execution plans, ER diagrams, scheduled backups, SQL stress testing with p50–p99 latency percentiles, a 15-rule SQL review engine, Kafka message browsing with monitoring & alerts, a bilingual UI (Traditional Chinese / English), a built-in AI assistant (natural-language SQL, AI review and tuning advice) and the dbk CLI. Free and open source (MIT), with installers for Windows, macOS and Linux.
- [VS Code Super Mermaid](https://blog.markkulab.net/en/tools/super-mermaid): Super Mermaid is a VS Code extension for beautiful Mermaid diagrams out of the box: auto-colored live preview, mouse pan & zoom, high-res PNG / SVG export, 21 templates and multiple themes. Free and open source (MIT).
- [React Super Mermaid](https://blog.markkulab.net/en/tools/react-super-mermaid): react-super-mermaid is an open-source React component library: render beautiful Mermaid diagrams with a single <MermaidViewer>, with built-in colorful / sketch themes, pan & zoom, in-diagram search, and high-res SVG / PNG export. Lightweight, SSR-safe, fully typed. Free and open source (MIT).
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/en/tools/jira-super-mermaid): An Atlassian Forge app: write Mermaid syntax directly inside a Jira issue or a Confluence page and get flowcharts, sequence diagrams, state machines and Gantt charts. 11 diagram types, SVG / PNG export, light and dark themes, full CJK support. Runs on Atlassian: your diagrams live in your own site and the app calls no third-party service. Free, coming soon to the Atlassian Marketplace.
- [Mermaid Live Preview](https://blog.markkulab.net/en/tools/mermaid-preview): Write Mermaid in your browser, see it render instantly, and share the whole diagram as a single link. No sign-up, nothing uploaded to a server, and mermaid.live share links work as-is.
- [React Intl Phone Number](https://blog.markkulab.net/en/tools/react-intl-phone-number): react-intl-phone-number is an open-source React component: framework-agnostic and antd-free, with E.164 in/out, a searchable flag / country-code dropdown, configurable validation levels (strict / mobile-strict / loose), themeable CSS, and i18n — phone logic powered by google-libphonenumber. Lightweight and fully typed. Free and open source (MIT).
- [Uptime Kuma Cluster](https://blog.markkulab.net/en/tools/uptime-kuma-cluster): Turn single-node Uptime Kuma into a highly available cluster: OpenResty + Lua smart load balancing, shared MariaDB state, health checks and automatic failover, plus cluster-management REST APIs. One Docker Compose command to start. Free and open source (MIT).
- [Special Education](https://blog.markkulab.net/en/education): Learning materials crafted for special education students

### Daily podcasts

- [Mark's Tech Insights — Daily AI News](https://blog.markkulab.net/en/category/tech-news): Daily curated AI and tech trends. Catch the latest developments via audio summaries — covering AI applications, software architecture, DevOps, and engineering practice. — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/en/category/ai-stock-chat): Every trading day, an AI-analyzed take on the Taiwan stock market, delivered as a two-host conversation covering the session and the next-day outlook. — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml
- [開源好物週報](https://blog.markkulab.net/en/category/open-source-weekly): A weekly two-host pick of free open-source tools surfaced from real Hacker News, GitHub, and Reddit buzz — what pain they solve and the fastest way to get started. — RSS: https://blog.markkulab.net/open-source-weekly/feed.xml

### Newsletter

[Subscribe to the newsletter](https://blog.markkulab.net/en/subscribe) — Be the first to know about new posts. No spam, unsubscribe anytime.
