Helm is the package manager for Kubernetes. It bundles manifests into reusable Charts with configurable values.yaml. Key commands: helm install (deploy), helm upgrade (update), helm rollback (revert), helm uninstall (remove). Charts use Go templates with built-in objects (.Values, .Release, .Chart). Use Helmfile for multi-release management, chart repositories (Artifact Hub, Harbor, ChartMuseum) for sharing, and helm-secrets or Cosign for security. Helm excels at complex multi-environment deployments; Kustomize is simpler for overlay-based patching.
- Helm packages Kubernetes manifests into reusable Charts with configurable values and versioned releases.
- Core commands: helm install, helm upgrade, helm rollback, helm uninstall, helm list, helm repo, helm search.
- Charts use Go template syntax with built-in objects: .Values, .Release, .Chart, .Capabilities.
- Override values via -f values-file.yaml or --set key=value; multiple sources merge with defined precedence.
- Hooks (pre-install, post-upgrade, etc.) run Jobs at lifecycle points for migrations, backups, and tests.
- Helmfile manages multiple releases declaratively; chart repos (Harbor, ChartMuseum) enable team sharing.
- Helm excels at parameterized packaging; Kustomize excels at overlay patching. Many teams use both.
What Is Helm?
Helm is the package manager for Kubernetes. Just as apt manages packages on Debian, npm manages packages for Node.js, and Homebrew manages packages on macOS, Helm manages packages for Kubernetes clusters. A Helm package is called a Chart โ a collection of Kubernetes manifest templates bundled with default configuration values and metadata.
Without Helm, deploying a production application to Kubernetes means writing and maintaining dozens of YAML files โ Deployments, Services, ConfigMaps, Secrets, Ingress, PersistentVolumeClaims, ServiceAccounts, RBAC roles, and more. When you need the same application in dev, staging, and production with different configurations, you end up duplicating YAML or writing brittle sed scripts. Helm solves this by parameterizing your manifests: you write templates once, and customize each deployment through a values file.
Helm v3 (the current major version since 2019) removed the server-side Tiller component that was required in v2, making Helm a purely client-side tool. It stores release state as Kubernetes Secrets in the target namespace, uses three-way strategic merge patches for upgrades, and supports OCI registries for chart distribution.
Installing Helm
Helm is distributed as a single binary. Install it on any platform and verify the installation with helm version.
# โโ macOS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
brew install helm
# โโ Linux (official script) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# โโ Linux (snap) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
sudo snap install helm --classic
# โโ Windows (Chocolatey) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
choco install kubernetes-helm
# โโ Windows (Scoop) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
scoop install helm
# โโ Verify installation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm version
# version.BuildInfo{Version:"v3.16.x", ...}
# โโ Shell completion โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm completion bash > /etc/bash_completion.d/helm
helm completion zsh > "${fpath[1]}/_helm"Helm Core Concepts: Charts, Releases, and Repositories
Charts
A Chart is a directory (or .tgz archive) containing all the Kubernetes resource definitions needed to run an application. Every chart has a Chart.yaml (metadata), a values.yaml (default configuration), and a templates/ directory containing Go-templated Kubernetes manifests.
Releases
A Release is a running instance of a Chart in a Kubernetes cluster. When you run helm install my-app ./my-chart, Helm creates a release named "my-app". You can install the same chart multiple times with different release names and different values, each producing an independent set of Kubernetes resources. Each release has a revision history, enabling upgrades and rollbacks.
Repositories
A Repository is an HTTP server or OCI registry that hosts packaged charts. Artifact Hub (artifacthub.io) is the public discovery portal aggregating charts from hundreds of repositories. You can also run private repositories using Harbor, ChartMuseum, or any OCI-compatible registry (Docker Hub, GitHub Container Registry, AWS ECR).
# Helm Concepts Overview
#
# Chart (Package) โโinstallโโโถ Release (Instance)
# Chart.yaml my-app (rev 1)
# values.yaml โโupgradeโโโถ my-app (rev 2)
# templates/
# deployment.yaml โโrollbackโโโถ my-app (rev 1)
# service.yaml
# ingress.yaml โโuninstallโโโถ (deleted)
#
# Repository: where charts are stored and shared
# artifacthub.io | Harbor | ChartMuseum | OCI RegistryEssential Helm Commands
Repository Management
# Add a chart repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add jetstack https://charts.jetstack.io
# Update local repo index
helm repo update
# List configured repos
helm repo list
# Search for charts
helm search repo redis # Search local repos
helm search hub wordpress # Search Artifact Hub
helm search repo bitnami/postgresql --versions # All versionsInstall, Upgrade, Rollback, Uninstall
# โโ Install a chart โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm install my-redis bitnami/redis \
--namespace caching --create-namespace \
--set auth.password=supersecret \
--set replica.replicaCount=3
# Install with custom values file
helm install my-app ./my-chart -f prod-values.yaml
# Dry-run to preview (no actual install)
helm install my-app ./my-chart --dry-run --debug
# โโ List releases โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm list # Current namespace
helm list -A # All namespaces
helm list --failed # Only failed releases
# โโ Upgrade a release โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm upgrade my-redis bitnami/redis \
--set replica.replicaCount=5
helm upgrade my-app ./my-chart -f prod-values.yaml
# Install if not exists, upgrade if exists
helm upgrade --install my-app ./my-chart -f values.yaml
# โโ Rollback โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm history my-redis # Show revision history
helm rollback my-redis 1 # Rollback to revision 1
helm rollback my-redis 0 # Rollback to previous
# โโ Uninstall โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm uninstall my-redis
helm uninstall my-redis --keep-history # Keep release historyInspect and Debug
# Show chart info
helm show chart bitnami/redis # Chart.yaml metadata
helm show values bitnami/redis # Default values.yaml
helm show readme bitnami/redis # README
helm show all bitnami/redis # Everything
# View release details
helm get values my-redis # User-supplied values
helm get values my-redis --all # All values (merged)
helm get manifest my-redis # Rendered K8s manifests
helm get notes my-redis # Post-install notes
# Render templates locally without installing
helm template my-app ./my-chart -f values.yaml
helm template my-app ./my-chart --debug # With debug infoCreating Your Own Helm Chart
Use helm create to scaffold a new chart. This generates a complete directory structure with best-practice defaults including a Deployment, Service, Ingress, ServiceAccount, and HPA templates.
# Scaffold a new chart
helm create my-app
# Generated structure:
my-app/
Chart.yaml # Chart metadata (name, version, deps)
values.yaml # Default configuration values
charts/ # Dependency charts (subcharts)
templates/ # Go-templated Kubernetes manifests
deployment.yaml
service.yaml
ingress.yaml
serviceaccount.yaml
hpa.yaml
NOTES.txt # Post-install instructions
_helpers.tpl # Reusable template snippets
tests/
test-connection.yamlChart.yaml
apiVersion: v2
name: my-app
description: A Helm chart for My Application
type: application # "application" or "library"
version: 0.1.0 # Chart version (SemVer)
appVersion: "1.16.0" # App version (informational)
keywords:
- web
- api
maintainers:
- name: DevOps Team
email: devops@example.com
dependencies:
- name: postgresql
version: "~13.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabledvalues.yaml
# Default values for my-app
replicaCount: 2
image:
repository: myregistry.io/my-app
tag: "latest"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: false
host: my-app.example.com
tls: []
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
value: info
postgresql:
enabled: true
auth:
database: myapp
username: appuserHelm Template Language
Helm uses the Go template engine (text/template) extended with Sprig functions and custom Helm functions. Templates are rendered before being sent to Kubernetes, producing valid YAML manifests. Understanding the template language is essential for creating flexible, production-grade charts.
Built-in Objects
- .Values โ user-supplied configuration merged from values.yaml, -f files, and --set flags.
- .Release โ release metadata: .Release.Name, .Release.Namespace, .Release.Revision, .Release.IsUpgrade, .Release.IsInstall.
- .Chart โ contents of Chart.yaml: .Chart.Name, .Chart.Version, .Chart.AppVersion.
- .Capabilities โ cluster info: .Capabilities.KubeVersion, .Capabilities.APIVersions.
- .Template โ current template info: .Template.Name, .Template.BasePath.
- .Files โ access non-template files in the chart: .Files.Get, .Files.GetBytes, .Files.AsConfig.
Template Syntax and Functions
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.port }}
{{- if .Values.resources }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- end }}
{{- if .Values.env }}
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- end }}Pipelines and Sprig Functions
# โโ Pipelines: chain functions with | โโโโโโโโโโโโโโโโโโโโโโโโโ
{{ .Values.name | upper | quote }} # "MY-APP"
{{ .Values.name | default "fallback" }} # Use fallback if empty
{{ .Values.port | int }} # Cast to integer
# โโ String functions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{{ trim .Values.name }}
{{ trimSuffix "-" .Values.name }}
{{ printf "%s-%s" .Release.Name .Chart.Name }}
{{ .Values.name | replace "-" "_" }}
# โโ Conditionals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}
{{- if and .Values.tls.enabled .Values.tls.secret }}
tls:
- secretName: {{ .Values.tls.secret }}
{{- else if .Values.tls.enabled }}
tls:
- secretName: {{ .Release.Name }}-tls
{{- end }}
# โโ Range (iteration) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
{{- end }}
{{- end }}
# โโ toYaml for nested structures โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}Named Templates (_helpers.tpl)
# templates/_helpers.tpl
{{- define "my-app.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
{{ include "my-app.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}Values and Overrides
Helm merges values from multiple sources with a defined precedence. Understanding this merge order is critical for managing per-environment configurations correctly.
# Value precedence (lowest to highest):
# 1. Chart default values.yaml
# 2. Parent chart values (if subchart)
# 3. -f / --values files (left to right)
# 4. --set / --set-string / --set-file flags
# โโ Per-environment values files โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# values.yaml (defaults)
# values-dev.yaml (dev overrides)
# values-staging.yaml (staging overrides)
# values-prod.yaml (production overrides)
# Deploy to staging:
helm upgrade --install my-app ./my-chart \
-f values.yaml \
-f values-staging.yaml \
--namespace staging
# Deploy to production with an extra override:
helm upgrade --install my-app ./my-chart \
-f values.yaml \
-f values-prod.yaml \
--set image.tag=v2.5.0 \
--namespace production
# View all merged values for a release:
helm get values my-app --all
# Show chart defaults:
helm show values bitnami/redis > redis-defaults.yamlDependencies and Subcharts
Charts can depend on other charts. Dependencies are declared in Chart.yaml and downloaded into the charts/ directory. This lets you compose complex applications (e.g., your app + PostgreSQL + Redis) as a single deployable unit.
# Chart.yaml dependencies section
dependencies:
- name: postgresql
version: "~13.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: redis
version: "~18.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
- name: common
version: "~2.0"
repository: https://charts.bitnami.com/bitnami
tags:
- infrastructure
# โโ Manage dependencies โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm dependency update ./my-chart # Download deps to charts/
helm dependency list ./my-chart # List deps and status
helm dependency build ./my-chart # Rebuild Chart.lock
# โโ Override subchart values in parent values.yaml โโโโโโโโโโโ
# values.yaml
postgresql:
enabled: true
auth:
database: myapp
username: appuser
password: secret123
primary:
persistence:
size: 20Gi
redis:
enabled: true
auth:
enabled: false
replica:
replicaCount: 2Helm Hooks
Hooks let you run operations at specific points in the release lifecycle. They are standard Kubernetes resources with special annotations that tell Helm when to execute them. Common use cases include database migrations before an upgrade, cache warming after an install, and backups before a delete.
- pre-install โ runs after templates are rendered but before any resources are created.
- post-install โ runs after all resources are created and ready.
- pre-upgrade โ runs before an upgrade (ideal for database migrations and backups).
- post-upgrade โ runs after upgrade is complete.
- pre-delete / post-delete โ runs before/after release deletion.
- pre-rollback / post-rollback โ runs before/after a rollback operation.
- test โ runs when
helm testis invoked.
# templates/db-migration-hook.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-db-migrate
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "0" # Lower runs first
"helm.sh/hook-delete-policy": before-hook-creation
spec:
backoffLimit: 3
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["npm", "run", "db:migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "my-app.fullname" . }}-db
key: url
# Hook delete policies:
# before-hook-creation - delete previous hook before new one
# hook-succeeded - delete after success
# hook-failed - delete after failureChart Testing
Helm supports built-in testing via the helm test command. Test resources are annotated with helm.sh/hook: test and typically verify that the deployed application is reachable and functioning correctly.
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: {{ include "my-app.fullname" . }}-test
annotations:
"helm.sh/hook": test
spec:
restartPolicy: Never
containers:
- name: wget
image: busybox:latest
command:
- /bin/sh
- -c
- |
wget -qO- http://{{ include "my-app.fullname" . }}:{{ .Values.service.port }}/health
if [ \$? -eq 0 ]; then echo "PASS"; else echo "FAIL" && exit 1; fi
# Run tests
helm test my-app
helm test my-app --logs # Show test pod logs
# โโ Lint and validate โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm lint ./my-chart # Check chart for issues
helm lint ./my-chart -f values-prod.yaml # Lint with values
# Validate rendered templates with kubeval or kubeconform
helm template my-app ./my-chart | kubeconform -strictHelmfile: Managing Multiple Releases
Helmfile is a declarative specification for deploying multiple Helm charts. It defines a desired state of Helm releases and lets you apply, diff, and destroy them as a group. Think of it as a Terraform for Helm releases โ you declare what you want, and Helmfile reconciles the cluster to match.
# helmfile.yaml
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
- name: ingress-nginx
url: https://kubernetes.github.io/ingress-nginx
environments:
dev:
values:
- env/dev.yaml
staging:
values:
- env/staging.yaml
production:
values:
- env/production.yaml
releases:
- name: ingress
chart: ingress-nginx/ingress-nginx
namespace: ingress-system
version: 4.10.x
values:
- ingress-values.yaml
- name: my-api
chart: ./charts/my-api
namespace: {{ .Environment.Name }}
values:
- charts/my-api/values.yaml
- charts/my-api/values-{{ .Environment.Name }}.yaml
set:
- name: image.tag
value: {{ requiredEnv "IMAGE_TAG" }}
- name: database
chart: bitnami/postgresql
namespace: {{ .Environment.Name }}
version: 13.x
condition: database.enabled
values:
- db-values.yaml
# โโ Helmfile commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# helmfile -e staging sync # Apply all releases
# helmfile -e production diff # Preview changes
# helmfile -e dev destroy # Remove all releases
# helmfile -e staging apply # Sync with confirmation
# helmfile -l name=my-api sync # Sync specific releaseChart Repositories: Harbor, ChartMuseum, and OCI
For teams that need private chart hosting, several options exist beyond the public Artifact Hub. Harbor provides enterprise-grade registry features. ChartMuseum is a lightweight chart server. Modern Helm also supports OCI registries natively.
# โโ ChartMuseum (lightweight chart server) โโโโโโโโโโโโโโโโโโโ
# Install ChartMuseum
helm repo add chartmuseum https://chartmuseum.github.io/charts
helm install chartmuseum chartmuseum/chartmuseum \
--set env.open.DISABLE_API=false \
--set persistence.enabled=true
# Push a chart to ChartMuseum
helm package ./my-chart
curl --data-binary "@my-chart-0.1.0.tgz" \
http://chartmuseum.example.com/api/charts
# โโ OCI Registry (Docker Hub, GHCR, ECR, Harbor) โโโโโโโโโโโโ
# Login to registry
helm registry login ghcr.io -u USERNAME
# Package and push
helm package ./my-chart
helm push my-chart-0.1.0.tgz oci://ghcr.io/myorg/charts
# Pull and install from OCI
helm pull oci://ghcr.io/myorg/charts/my-chart --version 0.1.0
helm install my-app oci://ghcr.io/myorg/charts/my-chart \
--version 0.1.0
# โโ Harbor (enterprise registry) โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm repo add harbor https://harbor.example.com/chartrepo/myproject
helm push my-chart-0.1.0.tgz harborHelm with CI/CD: GitHub Actions and ArgoCD
GitHub Actions
# .github/workflows/helm-deploy.yaml
name: Deploy with Helm
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.16.0
- name: Configure kubeconfig
run: |
mkdir -p \$HOME/.kube
echo "\${{ secrets.KUBECONFIG }}" > \$HOME/.kube/config
- name: Helm lint
run: helm lint ./charts/my-app
- name: Deploy to staging
run: |
helm upgrade --install my-app ./charts/my-app \
-f charts/my-app/values-staging.yaml \
--set image.tag=\${{ github.sha }} \
--namespace staging \
--wait --timeout 5mArgoCD with Helm
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app.git
targetRevision: main
path: charts/my-app
helm:
valueFiles:
- values-production.yaml
parameters:
- name: image.tag
value: v2.5.0
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueHelm Security: Signing and Provenance
Helm supports chart signing and verification using GnuPG (GPG) keys. A provenance file (.prov) is generated alongside the chart archive, containing a cryptographic signature and a SHA-256 hash of the chart. This ensures chart integrity and authenticity.
# โโ Sign a chart with GPG โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Generate a GPG key (if you do not have one)
gpg --full-generate-key
# Package and sign
helm package --sign --key "DevOps Team" \
--keyring ~/.gnupg/secring.gpg ./my-chart
# Produces: my-chart-0.1.0.tgz + my-chart-0.1.0.tgz.prov
# โโ Verify a chart โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
helm verify my-chart-0.1.0.tgz
helm install --verify my-app my-chart-0.1.0.tgz
# โโ OCI + Cosign (sigstore) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Modern alternative using keyless signing
cosign sign ghcr.io/myorg/charts/my-chart:0.1.0
cosign verify ghcr.io/myorg/charts/my-chart:0.1.0
# โโ Helm Secrets plugin (encrypted values) โโโโโโโโโโโโโโ
helm plugin install https://github.com/jkroepke/helm-secrets
# Encrypt a values file with SOPS + age
sops --encrypt --age age1abc123... values-secrets.yaml \
> values-secrets.enc.yaml
# Deploy with encrypted values
helm secrets upgrade my-app ./my-chart \
-f values.yaml \
-f values-secrets.enc.yamlHelm Best Practices
- Pin chart versions โ always specify
--versionwhen installing from repositories. Never rely on "latest" in production. - Use helm upgrade --install โ this idempotent command installs if the release does not exist and upgrades if it does. Ideal for CI/CD.
- Always pass --wait and --timeout โ
--waitmakes Helm wait until all resources are ready before marking the release as successful.--timeout 5mprevents indefinite hangs. - Keep values.yaml minimal โ only include parameters that users genuinely need to customize. Document each value with comments.
- Use _helpers.tpl โ extract reusable template logic (names, labels, selectors) into named templates to avoid repetition and ensure consistency.
- Run helm lint and helm template โ lint catches structural issues and template renders manifests locally for inspection before deploying.
- Version your charts with SemVer โ bump the chart version on every change. Use appVersion to track the application version independently.
- Use --atomic for safety โ
--atomicautomatically rolls back a failed upgrade, preventing broken releases in production. - Separate secrets from values โ use helm-secrets or external secret managers (Vault, AWS Secrets Manager, SOPS) instead of putting secrets in plain values files.
- Test your charts โ write
helm testhooks, use ct (chart-testing) for CI, and validate rendered output with kubeconform.
Helm vs Kustomize
Helm and Kustomize are the two dominant approaches to managing Kubernetes configurations. They solve overlapping but different problems, and many teams use both.
| Aspect | Helm | Kustomize |
|---|---|---|
| Approach | Templating + packaging | Overlay patching (template-free) |
| Language | Go templates + Sprig | Plain YAML + kustomization.yaml |
| Packaging | Charts (.tgz) with repos | Directories (no packaging) |
| Release management | Built-in (history, rollback) | None (relies on kubectl / GitOps) |
| Conditionals / loops | Full (if/else, range, with) | None |
| kubectl native | No (separate binary) | Yes (kubectl apply -k) |
| Best for | Community charts, complex apps, multi-env | Simple overlays, in-house apps |
# โโ Kustomize example for comparison โโโโโโโโโโโโโโโโโโโโโโโโโโ
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
kind: Deployment
name: my-app
patch: |-
- op: replace
path: /spec/replicas
value: 5
images:
- name: my-app
newTag: v2.5.0
# Apply with kubectl
kubectl apply -k overlays/production/
# โโ Using both together โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Render Helm templates, then apply Kustomize patches
helm template my-app ./my-chart -f values.yaml > rendered.yaml
# Add rendered.yaml to Kustomize base, then overlayFrequently Asked Questions
What is Helm and why do I need it?
Helm bundles Kubernetes manifests into reusable Charts. Use it for complex multi-resource deployments, per-environment configuration, and installing community software from Artifact Hub.
Chart vs Release?
A Chart is a package (templates + values). A Release is a running instance in your cluster. The same Chart can be installed multiple times with different names and configurations.
How do I install Helm?
macOS: brew install helm. Linux: official install script or snap install helm --classic. Windows: choco install kubernetes-helm. Verify with helm version.
How does the template language work?
Go template syntax with Sprig functions. Access .Values, .Release, .Chart, and .Capabilities objects. Supports conditionals, loops, pipelines, and named templates in _helpers.tpl.
How do I override values?
Precedence: chart defaults < parent chart < -f files (left to right) < --set flags. Use helm show values to discover available parameters.
What are Helm hooks?
Resources annotated with helm.sh/hook that run at lifecycle points (pre-install, pre-upgrade, post-upgrade, test, etc.). Used for DB migrations, backups, and smoke tests.
What is Helmfile?
A declarative tool for managing multiple Helm releases in one helmfile.yaml with environment support, dependencies, and synchronized apply/destroy.
Helm or Kustomize?
Helm for reusable packages, community charts, and release lifecycle. Kustomize for overlay patching and kubectl-native workflows. Many teams use both together.
Conclusion
Helm is an essential tool in the Kubernetes ecosystem. It transforms the complexity of managing dozens of YAML manifests into reusable, versioned, configurable packages. Whether you are deploying a simple web application or orchestrating a complex microservices architecture with databases, caches, and ingress controllers, Helm provides the packaging, release management, and ecosystem that make Kubernetes deployments repeatable and reliable.
Start with helm create to scaffold your first chart, learn the template language incrementally, and adopt best practices like version pinning, helm lint, and --atomic upgrades. As your infrastructure grows, add Helmfile for multi-release management, chart repositories for team sharing, and chart signing for security. The investment in learning Helm pays dividends across every Kubernetes project you touch.