Skip to content

Helm Charts

On this page

Helm is the package manager for Kubernetes. Charts template your manifests, values parameterize them, and releases track what's installed so you can upgrade and roll back.

Basics

Helm packages Kubernetes manifests into versioned, reusable units called charts. Instead of hand-editing dozens of YAML files per environment, you template them once and supply different values.

Key terms

Term Meaning
Chart A package of templated K8s manifests + metadata + default values.
Values Configuration that fills the templates (values.yaml, --set, -f).
Release An installed instance of a chart in a cluster (named, versioned, revisioned).
Repository A place to host/share charts (HTTP or OCI registry).
Template A manifest with Go templating + Sprig functions.

Helm 3 is client-only (no Tiller); it talks straight to the Kubernetes API and stores release state as Secrets in the cluster.

Chart structure

mychart/
├── Chart.yaml          # name, version, appVersion, dependencies
├── values.yaml         # default configuration
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── _helpers.tpl    # reusable template snippets
│   └── NOTES.txt       # post-install message
├── charts/             # vendored sub-charts (dependencies)
└── templates/tests/    # `helm test` hooks

Templating

Templates use {{ .Values.x }}, {{ .Release.Name }}, {{ .Chart.Name }}, control flow (if/range/with), named templates (define/include), and Sprig helper functions.

# templates/deployment.yaml (excerpt)
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

Cheatsheet

# Repos (classic HTTP repo)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo nginx
helm search hub wordpress              # search Artifact Hub

# Install / upgrade / rollback
helm install web ./mychart -n app --create-namespace
helm install web bitnami/nginx -f prod-values.yaml
helm upgrade --install web ./mychart -n app \
  --set replicaCount=3 --atomic --wait
helm list -n app
helm status web -n app
helm history web -n app
helm rollback web 2 -n app             # revert to revision 2
helm uninstall web -n app

# Author & debug
helm create mychart
helm lint ./mychart
helm template web ./mychart -f prod-values.yaml   # render locally, no cluster
helm install web ./mychart --dry-run --debug
helm get values web -n app
helm get manifest web -n app

# Dependencies
helm dependency update ./mychart       # pull sub-charts in Chart.yaml

# OCI registries (modern)
helm push mychart-1.0.0.tgz oci://ghcr.io/moin/charts
helm install web oci://ghcr.io/moin/charts/mychart --version 1.0.0

Thumb Rules

Rules of thumb

  • helm upgrade --install is your default — it works whether or not the release exists.
  • Use --atomic --wait so a failed upgrade rolls back automatically instead of leaving a half-broken release.
  • helm template / --dry-run --debug before applying to see exactly what will be created.
  • Pin chart versions (--version) in CI; don't float on whatever's latest.
  • Keep one values.yaml per environment and layer with -f base.yaml -f prod.yaml.
  • Don't put secrets in plain values.yaml. Use helm-secrets/SOPS or external secret stores.
  • appVersion ≠ chart version. Bump chart version on any template change.
  • Prefer upstream charts you understand; vendor/fork rather than over-patch with --set sprawl.

Use Cases

  • Installing off-the-shelf software (databases, ingress controllers, monitoring stacks) in one command.
  • Packaging your own apps for repeatable, multi-environment deploys.
  • Templating across environments (dev/stage/prod) from one chart + many values files.
  • Release lifecycle management — versioned upgrades and instant rollbacks.
  • GitOps building block — Argo CD/Flux render Helm charts as part of pipelines.
  • Umbrella charts — compose a whole platform from sub-charts.

Common Issues

UPGRADE FAILED / release stuck in pending-upgrade

A previous upgrade was interrupted. Inspect helm history, then helm rollback to the last good revision. Using --atomic prevents getting stuck here.

Template renders wrong / nil pointer evaluating interface

A value referenced in a template isn't set. Provide a default (| default), guard with if, and validate with helm template --debug.

Indentation / YAML errors after templating

Misused nindent/indent, or toYaml without proper indentation. Render locally and eyeball the output; nindent N adds a newline + N spaces.

Immutable field error on upgrade

Changing an immutable field (e.g. a Deployment selector, a PVC size on some StorageClasses). You may need to delete/recreate the resource.

CRDs not installed / out of date

Helm installs CRDs from crds/ only on first install and won't upgrade them. Manage CRD upgrades explicitly.

Resources left behind after uninstall

Resources with helm.sh/resource-policy: keep, or CRDs/PVCs Helm intentionally doesn't delete. Clean up manually if needed.

Best Practices

  • Default to helm upgrade --install --atomic --wait for safe, idempotent deploys.
  • Lint and helm template in CI; add helm test hooks for smoke tests.
  • Version charts semantically; keep a changelog; pin versions in deployments.
  • Layer values files per environment; keep secrets out of Git (SOPS/External Secrets).
  • Use _helpers.tpl for consistent labels/names; follow the recommended label conventions.
  • Vendor dependencies with a lockfile (Chart.lock) for reproducibility.
  • Prefer OCI registries for distributing charts alongside images.
  • Document values.yaml thoroughly — it's your chart's public API.

Official Sources