Getting started
Introduction
Helm is the package manager for Kubernetes, providing templating, versioning, and dependency management for Kubernetes applications.
- Client-only architecture (no Tiller since v3)
- Namespace-scoped releases (Helm 3+)
- Latest version: v4.0.0 (2026)
- Storage: Kubernetes Secrets by default
Installation
macOS:
brew install helm
Linux (Debian/Ubuntu):
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
Snap:
sudo snap install helm --classic
Binary download:
curl -LO "https://get.helm.sh/helm-v4.0.0-linux-amd64.tar.gz"
tar -zxvf helm-v4.0.0-linux-amd64.tar.gz
sudo mv linux-amd64/helm /usr/local/bin/helm
Quick Example
# Add a chart repository
helm repo add bitnami https://charts.bitnami.com/bitnami
# Update repository index
helm repo update
# Install a chart
helm install my-release bitnami/nginx
# Check release status
helm status my-release
# Upgrade release
helm upgrade my-release bitnami/nginx --set service.type=LoadBalancer
# Rollback to previous version
helm rollback my-release
# Uninstall release
helm uninstall my-release
Version History
| Version | Key Changes |
|---|---|
| Helm v2 | Tiller server component (EOL Nov 2020) |
| Helm v3 | Removed Tiller, Secrets storage, chart apiVersion v2 |
| Helm v4 | Plugin overhaul, server-side apply, OCI digest support |
Configuration Directories
| Path | Purpose |
|---|---|
~/.cache/helm |
Cache |
~/.config/helm |
Configuration |
~/.local/share/helm |
Data (Linux) |
Core Commands
Install
# Basic install
helm install RELEASE_NAME CHART_NAME
# Install with custom values
helm install RELEASE_NAME CHART_NAME -f values.yaml
# Install with --set
helm install RELEASE_NAME CHART_NAME --set key=value
# Auto-generated name
helm install CHART_NAME --generate-name
# Specific namespace
helm install RELEASE_NAME CHART_NAME -n NAMESPACE --create-namespace
# Dry-run (test without installing)
helm install RELEASE_NAME CHART_NAME --dry-run --debug
# Rollback on failure (v4)
helm install RELEASE_NAME CHART_NAME --rollback-on-failure
# Wait for ready
helm install RELEASE_NAME CHART_NAME --wait --timeout 5m
# Wait for jobs
helm install RELEASE_NAME CHART_NAME --wait --wait-for-jobs
Upgrade
# Basic upgrade
helm upgrade RELEASE_NAME CHART_NAME
# Install if not exists
helm upgrade --install RELEASE_NAME CHART_NAME
# Upgrade with new values
helm upgrade RELEASE_NAME CHART_NAME -f new-values.yaml
# Reuse existing values
helm upgrade RELEASE_NAME CHART_NAME --reuse-values --set key=newvalue
# Reset then reuse values
helm upgrade RELEASE_NAME CHART_NAME --reset-then-reuse-values --set key=value
# Force resource replacement (v4)
helm upgrade RELEASE_NAME CHART_NAME --force-replace
# Cleanup on failure
helm upgrade RELEASE_NAME CHART_NAME --cleanup-on-fail
# Dry-run upgrade
helm upgrade RELEASE_NAME CHART_NAME --dry-run --debug
Rollback
# Rollback to previous revision
helm rollback RELEASE_NAME
# Rollback to specific revision
helm rollback RELEASE_NAME REVISION_NUMBER
# Rollback to previous (explicit)
helm rollback RELEASE_NAME 0
Uninstall
# Uninstall release
helm uninstall RELEASE_NAME
# Uninstall and keep history
helm uninstall RELEASE_NAME --keep-history
# Uninstall from namespace
helm uninstall RELEASE_NAME -n NAMESPACE
List
# List releases
helm list
# All namespaces
helm list --all-namespaces
helm list -A
# Include deleted
helm list --all
# Filter by status
helm list --deployed
helm list --failed
helm list --pending
Status & History
# Show release status
helm status RELEASE_NAME
# Status as YAML/JSON
helm status RELEASE_NAME -o yaml
helm status RELEASE_NAME -o json
# Show revision history
helm history RELEASE_NAME
# History as YAML
helm history RELEASE_NAME -o yaml
Get
# Get deployed manifests
helm get manifest RELEASE_NAME
# Get computed values
helm get values RELEASE_NAME
# Get all values
helm get values RELEASE_NAME --all
# Get hooks
helm get hooks RELEASE_NAME
# Get post-install notes
helm get notes RELEASE_NAME
# Get everything
helm get all RELEASE_NAME
Repository Management
Add Repository
# Add chart repository
helm repo add REPO_NAME URL
# Add with force update
helm repo add REPO_NAME URL --force-update
# Examples
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
Update & List
# Update all repository indexes
helm repo update
# Update specific repository
helm repo update REPO_NAME
# List repositories
helm repo list
# Remove repository
helm repo remove REPO_NAME
Search
# Search Artifact Hub
helm search hub KEYWORD
# Search local repositories
helm search repo KEYWORD
# Search with versions
helm search repo KEYWORD --versions
# Search with regex
helm search repo --regexp 'nginx.*'
Chart Structure
Directory Layout
chart-name/
Chart.yaml # Chart metadata
values.yaml # Default config
charts/ # Dependencies
templates/ # Templates
templates/NOTES.txt # Post-install notes
crds/ # CRDs
README.md # Documentation
LICENSE # License
.helmignore # Exclude files
Chart.yaml
Required fields:
apiVersion: v2 # v2 for Helm 3+
name: my-chart # Chart name
version: 1.0.0 # Chart version (SemVer)
Optional fields:
kubeVersion: ">=1.20.0"
description: "My application"
type: application # or 'library'
keywords:
- web
- app
home: https://example.com
sources:
- https://github.com/example/repo
dependencies:
- name: postgresql
version: "12.1.9"
repository: https://charts.bitnami.com/bitnami
maintainers:
- name: John Doe
email: john@example.com
icon: https://example.com/icon.png
appVersion: "2.0.1" # App version
deprecated: false
Chart Types
| Type | Description |
|---|---|
application |
Default, installable chart |
library |
Utility templates only, not installable |
CRDs
Custom Resource Definitions have special handling:
- Stored in
crds/directory - Cannot be templated
- Installed before other resources
- Never upgraded by Helm
- Never deleted by Helm
Warning: Manage CRD upgrades manually outside Helm.
Chart Development
Create
# Scaffold new chart
helm create CHART_NAME
This creates:
- Chart.yaml
- values.yaml
- templates/ directory
- Sample Deployment, Service, Ingress
Lint
# Validate chart
helm lint CHART_PATH
# Lint with values
helm lint CHART_PATH --values values.yaml
Template
# Render templates locally
helm template RELEASE_NAME CHART_PATH
# Render with values
helm template RELEASE_NAME CHART_PATH -f values.yaml
# Show only specific template
helm template RELEASE_NAME CHART_PATH --show-only templates/deployment.yaml
# Output to directory
helm template RELEASE_NAME CHART_PATH --output-dir ./rendered
# Include CRDs
helm template RELEASE_NAME CHART_PATH --include-crds
# Debug rendering
helm template RELEASE_NAME CHART_PATH --debug
Package
# Package chart to .tgz
helm package CHART_PATH
# Package with version
helm package CHART_PATH --version 1.2.3
# Package and sign
helm package CHART_PATH --sign --key keyname --keyring ~/.gnupg/secring.gpg
Dependencies
# Download dependencies to charts/
helm dependency update CHART_PATH
# Rebuild Chart.lock
helm dependency build CHART_PATH
# List dependencies
helm dependency list CHART_PATH
Show
# Show chart metadata
helm show chart CHART_NAME
# Show default values
helm show values CHART_NAME
# Show README
helm show readme CHART_NAME
# Show all
helm show all CHART_NAME
Values & Configuration
Values Precedence
Highest to lowest:
--setflags--valuesfiles (rightmost wins)- Chart's default
values.yaml
Using --set
# Simple value
helm install myapp ./mychart --set key=value
# Nested value
helm install myapp ./mychart --set outer.inner=value
# Multiple values
helm install myapp ./mychart --set key1=val1,key2=val2
# List values
helm install myapp ./mychart --set list={a,b,c}
# Null value
helm install myapp ./mychart --set key=null
# Empty array
helm install myapp ./mychart --set list=[]
# Escape commas
helm install myapp ./mychart --set key=value\,with\,commas
Using --values
# Single values file
helm install myapp ./mychart -f values.yaml
# Multiple files (rightmost wins)
helm install myapp ./mychart -f values-base.yaml -f values-prod.yaml
# Values from URL
helm install myapp ./mychart --values https://example.com/values.yaml
Other Value Flags
# Force string type
helm install myapp ./mychart --set-string key=123
# Read from file
helm install myapp ./mychart --set-file config=config.txt
# JSON value
helm install myapp ./mychart --set-json 'config={"key":"value"}'
Predefined Values
Available in all templates:
.Release.Name # Release name
.Release.Namespace # Release namespace
.Release.Service # Always "Helm"
.Release.IsUpgrade # true if upgrade
.Release.IsInstall # true if install
.Chart.Name # Chart name
.Chart.Version # Chart version
.Chart.AppVersion # App version
.Values # Values object
.Files # File accessor
.Capabilities # Cluster capabilities
Global Values
# values.yaml
global:
domain: example.com
image:
registry: docker.io
# Accessible in all charts as:
# {{ .Values.global.domain }}
Debugging
Debug Commands
# Dry-run with debug
helm install myapp ./mychart --dry-run --debug
# Template locally
helm template myapp ./mychart
helm template myapp ./mychart --debug
# Get deployed manifests
helm get manifest RELEASE_NAME
# Get computed values
helm get values RELEASE_NAME
helm get values RELEASE_NAME --all
# Verbose logging (0-10)
helm list -v 6
Template Debugging
# Render all templates
helm template myapp ./mychart --debug
# Render specific template
helm template myapp ./mychart --show-only templates/deployment.yaml
# Save rendered output
helm template myapp ./mychart --output-dir ./rendered
# Check syntax only
helm lint ./mychart
Release Debugging
# Check release status
helm status RELEASE_NAME
# Check release history
helm history RELEASE_NAME
# Get all release info
helm get all RELEASE_NAME
# Check hooks
helm get hooks RELEASE_NAME
Troubleshooting
Release Already Exists
Error: cannot re-use a name that is still in use
Diagnosis:
# Check release status
helm list -a
helm status RELEASE_NAME
# Check history
helm history RELEASE_NAME
Solution:
# Uninstall first
helm uninstall RELEASE_NAME
# Or use upgrade --install
helm upgrade --install RELEASE_NAME CHART_NAME
Template Rendering Errors
Error: Error: template: mychart/templates/deployment.yaml:10:14: executing...
Diagnosis:
# Render locally
helm template myapp ./mychart --debug
# Show specific template
helm template myapp ./mychart --show-only templates/deployment.yaml
Common fixes:
- Undefined values:
{{ .Values.key | default "value" }} - Required values:
{{ required "msg" .Values.key }} - Check template syntax
Values Not Applied
Diagnosis:
# Check computed values
helm get values RELEASE_NAME
# Check all values
helm get values RELEASE_NAME --all
# Dry-run upgrade
helm upgrade RELEASE_NAME CHART_NAME --dry-run --debug
Common causes:
--setoverrides--values- Typo in key name
- Need
--reuse-valuesfor upgrades
Warning: --set always takes precedence over --values files.
Release Stuck Pending
States: pending-install, pending-upgrade, pending-rollback
Diagnosis:
# Check status
helm status RELEASE_NAME
# Check Kubernetes events
kubectl get events --sort-by='.lastTimestamp'
# Check pods
kubectl get pods -n NAMESPACE
kubectl describe pod POD_NAME -n NAMESPACE
Solution:
# Uninstall if stuck
helm uninstall RELEASE_NAME
# Check orphaned resources
kubectl get all -n NAMESPACE
Hook Failures
Diagnosis:
# Get hooks
helm get hooks RELEASE_NAME
# Check hook pods/jobs
kubectl get pods -n NAMESPACE -l 'helm.sh/hook'
kubectl logs POD_NAME -n NAMESPACE
Solution:
# Increase timeout
helm install RELEASE_NAME CHART_NAME --timeout 10m
# Clean up failed hooks
kubectl delete pod HOOK_POD_NAME -n NAMESPACE
Dependency Issues
Error: found in Chart.yaml, but missing in charts/ directory
Solution:
# Update dependencies
helm dependency update CHART_PATH
# Build dependencies
helm dependency build CHART_PATH
# List dependencies
helm dependency list CHART_PATH
Namespace Issues
Helm 3+ uses current kubectl namespace context.
Diagnosis:
# Check current namespace
kubectl config view --minify | grep namespace
# List all releases
helm list --all-namespaces
Solution:
# Specify namespace
helm install RELEASE_NAME CHART_NAME -n NAMESPACE
# Create if missing
helm install RELEASE_NAME CHART_NAME -n NAMESPACE --create-namespace
Info: Always verify namespace context with kubectl config get-contexts.
Helm Hooks
Hook Types
| Hook | When Executed |
|---|---|
pre-install |
Before resources installed |
post-install |
After all resources installed |
pre-delete |
Before resources deleted |
post-delete |
After all resources deleted |
pre-upgrade |
Before resources upgraded |
post-upgrade |
After all resources upgraded |
pre-rollback |
Before rollback |
post-rollback |
After rollback |
test |
When helm test invoked |
Hook Annotations
Declare hook:
metadata:
annotations:
"helm.sh/hook": post-install,post-upgrade
Hook weight (execution order, lower first):
metadata:
annotations:
"helm.sh/hook": post-install
"helm.sh/hook-weight": "5"
Deletion policy:
metadata:
annotations:
"helm.sh/hook": post-install
"helm.sh/hook-delete-policy": hook-succeeded,hook-failed
Deletion Policies
| Policy | Description |
|---|---|
before-hook-creation |
Delete previous resource before new hook (default) |
hook-succeeded |
Delete after successful execution |
hook-failed |
Delete if hook failed |
Hook Execution Order
- Sorted by weight (ascending)
- Then by kind
- Then by name
- Helm waits for "Ready" state
- For Job/Pod: waits for completion
- Hook failure causes release failure
Hook Example
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}-db-migrate"
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["/bin/sh", "-c", "migrate-db"]
restartPolicy: Never
Warning: Hooks are NOT part of release and NOT deleted by helm uninstall.
Advanced Features
Atomic Operations
# Rollback on failure (v4)
helm install RELEASE_NAME CHART_NAME --rollback-on-failure
# Legacy v3 flag
helm install RELEASE_NAME CHART_NAME --atomic
Wait Flags
# Wait for resources ready
helm install RELEASE_NAME CHART_NAME --wait --timeout 10m
# Wait for jobs
helm install RELEASE_NAME CHART_NAME --wait --wait-for-jobs
Force Replace
# Force replacement (v4)
helm upgrade RELEASE_NAME CHART_NAME --force-replace
# Legacy v3 flag
helm upgrade RELEASE_NAME CHART_NAME --force
Cleanup on Fail
# Delete new resources if upgrade fails
helm upgrade RELEASE_NAME CHART_NAME --cleanup-on-fail
Reuse Values
# Reuse existing values
helm upgrade RELEASE_NAME CHART_NAME --reuse-values
# Reset to chart defaults
helm upgrade RELEASE_NAME CHART_NAME --reset-values
# Reset then reuse
helm upgrade RELEASE_NAME CHART_NAME --reset-then-reuse-values
Warning: --reuse-values preserves previous --set overrides.
Test
# Run chart tests
helm test RELEASE_NAME
# Test with logs
helm test RELEASE_NAME --logs
Plugins
# Install plugin
helm plugin install PLUGIN_URL
# List plugins
helm plugin list
# Update plugin
helm plugin update PLUGIN_NAME
# Uninstall plugin
helm plugin uninstall PLUGIN_NAME
OCI Registry
# Install from OCI registry
helm install myapp oci://registry.example.com/charts/app --version 1.0.0
# Push chart to OCI
helm push mychart-1.0.0.tgz oci://registry.example.com/charts
Best Practices
Recommended Labels
metadata:
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
Chart Naming
- Use lowercase letters, numbers, hyphens
- Start with letter
- Examples:
my-app,postgresql,nginx-ingress
Values Organization
Good:
image:
repository: nginx # Docker image
tag: "1.21" # Image tag
pullPolicy: IfNotPresent
Bad:
imageRepository: nginx
imageTag: "1.21"
Documentation
- Include
README.mdwith install instructions - Document all
values.yamloptions - Use
templates/NOTES.txtfor post-install guidance - Include prerequisites and dependencies
Template Helpers
{{- define "mychart.labels" -}}
app: {{ .Chart.Name }}
version: {{ .Chart.Version }}
{{- end }}
# Use in templates:
metadata:
labels:
{{- include "mychart.labels" . | nindent 4 }}
Environment Variables
Storage Backend
# Use ConfigMaps
export HELM_DRIVER=configmap
# Default: Secrets
export HELM_DRIVER=secret
# Other: memory, sql
Configuration Paths
# Override config directory
export HELM_CONFIG_HOME=/path/to/config
# Override data directory
export HELM_DATA_HOME=/path/to/data
# Override cache directory
export HELM_CACHE_HOME=/path/to/cache
# Multiple kubeconfig
export KUBECONFIG=~/.kube/config1:~/.kube/config2
Debug
# Enable debug output
export HELM_DEBUG=true
Version Differences
v2 → v3 Migration
| Change | Description |
|---|---|
| Tiller removed | Client-only architecture |
| Namespace-scoped | Releases per namespace, not cluster-wide |
| Secrets storage | Default changed from ConfigMaps |
| Chart API v2 | New chart format |
| Release names | Unique per namespace only |
| Commands | delete → uninstall, inspect → show |
v3 → v4 Changes
| Change | Description |
|---|---|
| Plugin system | WebAssembly-based runtime |
| Flag renames | --atomic → --rollback-on-failure |
--force → --force-replace |
|
| Server-side apply | Default for resource updates |
| OCI digest | Full OCI registry support |
| Chart v3 | Coming soon (v2 still works) |
Quirks & Gotchas
Values Precedence
Warning: --set always overrides --values files, even if --values comes after.
- Multiple
--valuesfiles: rightmost wins --reuse-valuespreserves previous--setoverrides
Example:
# Initial install
helm install myapp ./chart --set replicas=5
# This upgrade keeps replicas=5 (not 3!)
helm upgrade myapp ./chart --reuse-values -f values.yaml # values.yaml has replicas: 3
Hook Lifecycle
Warning: Hooks are NOT part of the release.
- Not tracked by
helm list - Not deleted by
helm uninstall - Must clean up manually if persistent
- Hook failures cause release failure
CRD Limitations
Warning: CRDs in crds/ have special handling.
- Cannot be templated
- Never upgraded by Helm
- Never deleted by Helm
- Must manage upgrades manually
Example:
# CRDs upgraded manually
kubectl apply -f crds/
# Then upgrade chart
helm upgrade myapp ./chart
Namespace Context
Info: Helm 3+ uses current kubectl namespace.
- Releases are namespace-scoped
- Always verify:
kubectl config get-contexts - Use
-n NAMESPACEto be explicit
Storage Backend
- Default: Secrets (Helm 3+)
- Release data stored in cluster
- Large releases can hit Secret size limits (1MB)
- Consider ConfigMaps for very large releases
Change storage backend:
export HELM_DRIVER=configmap
Also see
- Helm Documentation - Official Helm documentation
- Helm Installation Guide - Installation methods
- Using Helm - Getting started guide
- Charts Guide - Chart development
- Hooks Documentation - Hook reference
- Best Practices - Chart best practices
- Helm Architecture - Architecture overview
- Helm 4 Overview - Latest version features
- Troubleshooting - Common issues
- Artifact Hub - Discover Helm charts