Helm Cheatsheet

Quick reference for Helm — the Kubernetes package manager. Covers installation, chart management, templating, OCI registries, and production patterns.

Quick Reference

Installation & Setup

# Install Helm (Linux/macOS)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# macOS via Homebrew
brew install helm

# Verify installation
helm version
helm env

# Set default namespace
export HELM_NAMESPACE=production

Repository Management

# Add popular repos
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo add jetstack https://charts.jetstack.io
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

# Update all repos
helm repo update

# List configured repos
helm repo list

# Remove a repo
helm repo remove stable

# Search in repos
helm search repo nginx
helm search repo bitnami/nginx --versions

Chart Operations

# Install a chart
helm install my-release bitnami/nginx -n my-ns

# Install with values file
helm install my-release bitnami/nginx -f values.yaml

# Upgrade (or install if not exists)
helm upgrade --install my-release bitnami/nginx \
  --namespace my-ns --create-namespace \
  -f values.yaml

# Uninstall a release
helm uninstall my-release -n my-ns

# Rollback to previous revision
helm rollback my-release 1 -n my-ns

# View release history
helm history my-release -n my-ns

Release Management

# List all releases (all namespaces)
helm list -A
helm list --all-namespaces

# Filter by state
helm list -A --deployed
helm list -A --failed
helm list -A --pending

# Get release status
helm status my-release -n my-ns

# Get deployed values
helm get values my-release -n my-ns
helm get values my-release -n my-ns --all  # includes defaults

# Get manifest / hooks / notes
helm get manifest my-release -n my-ns
helm get hooks my-release -n my-ns
helm get notes my-release -n my-ns

Templating & Debugging

# Render templates locally (no cluster)
helm template my-release ./mychart -f values.yaml

# Render with namespace context
helm template my-release ./mychart \
  --namespace production \
  --set image.tag=1.2.3

# Lint a chart
helm lint ./mychart
helm lint ./mychart -f values.yaml

# Dry run (server-side validation)
helm install my-release ./mychart --dry-run --debug

# Diff plugin (show what will change)
helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade my-release ./mychart -f values.yaml

Chart Development

# Create new chart scaffold
helm create mychart

# Chart directory structure
mychart/
  Chart.yaml          # Chart metadata
  values.yaml         # Default values
  charts/             # Chart dependencies
  templates/          # Kubernetes manifests
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl      # Named templates
    NOTES.txt         # Post-install notes
  .helmignore         # Files to exclude

# Package a chart
helm package ./mychart
helm package ./mychart --version 1.2.3

Show Chart Info

# Show default values
helm show values bitnami/nginx
helm show values bitnami/nginx > values-defaults.yaml

# Show chart metadata
helm show chart bitnami/nginx

# Show README
helm show readme bitnami/nginx

# Show all info
helm show all bitnami/nginx

# Pull chart tarball locally
helm pull bitnami/nginx
helm pull bitnami/nginx --untar
helm pull bitnami/nginx --version 15.0.0 --untar

OCI Registry

# Login to GHCR
helm registry login ghcr.io \
  -u USERNAME --password-stdin << EOF
ghp_TOKEN
EOF

# Login to ECR
aws ecr get-login-password --region us-east-1 \
  | helm registry login \
    --username AWS \
    --password-stdin \
    123456789.dkr.ecr.us-east-1.amazonaws.com

# Push chart to OCI registry
helm push mychart-1.0.0.tgz oci://ghcr.io/myorg/charts

# Pull from OCI registry
helm pull oci://ghcr.io/myorg/charts/mychart --version 1.0.0

# Install directly from OCI
helm install my-release \
  oci://ghcr.io/myorg/charts/mychart \
  --version 1.0.0

Values Override Patterns

Helm applies values in order of increasing precedence: chart defaults → -f file flags (left to right) → --set flags.

# --set: simple key=value (highest precedence)
helm upgrade --install my-release ./chart \
  --set image.repository=myrepo/myapp \
  --set image.tag=v1.2.3 \
  --set replicaCount=3

# --set-string: force string type (useful for numeric-looking values)
helm install my-release ./chart \
  --set-string image.tag=007 \
  --set-string annotations."app\.kubernetes\.io/version"=latest

# --set-json: pass JSON values (arrays, objects)
helm install my-release ./chart \
  --set-json 'tolerations=[{"key":"dedicated","operator":"Equal","value":"gpu"}]'

# --values / -f: load from file
helm upgrade --install my-release ./chart \
  -f values-base.yaml \
  -f values-production.yaml \
  --set image.tag=$(git rev-parse --short HEAD)

# Multiple -f flags (right file wins on conflict)
helm upgrade --install my-release ./chart \
  -f ./environments/base/values.yaml \
  -f ./environments/prod/values.yaml \
  -f ./secrets/prod-secrets.yaml

# Precedence order (lowest to highest):
# 1. Chart's values.yaml (defaults)
# 2. Parent chart's values.yaml
# 3. -f files (left to right)
# 4. --set / --set-string / --set-json (rightmost wins)

Complete Chart.yaml Example

apiVersion: v2
name: mychart
description: A Helm chart for my application
type: application          # or "library"
version: 1.2.3             # Chart version (SemVer)
appVersion: "2.5.1"        # App version (informational)

# Optional fields
icon: https://example.com/icon.png
home: https://example.com
sources:
  - https://github.com/myorg/myapp
keywords:
  - myapp
  - web
maintainers:
  - name: Binh Phuong
    email: [email protected]
    url: https://lebinhphuong.tech

annotations:
  artifacthub.io/changes: |
    - kind: added
      description: Added support for PodDisruptionBudget

# Chart dependencies (stored in charts/ after helm dep update)
dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
    tags:
      - database
  - name: redis
    version: "17.x.x"
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled

Helm Hooks

Hooks execute at specific points in a release lifecycle. Common uses: run DB migrations before upgrade, send notifications after install, clean up jobs before delete.

# Hook annotations reference
annotations:
  "helm.sh/hook": pre-install           # Before first install
  "helm.sh/hook": post-install          # After first install
  "helm.sh/hook": pre-upgrade           # Before upgrade
  "helm.sh/hook": post-upgrade          # After upgrade
  "helm.sh/hook": pre-delete            # Before uninstall
  "helm.sh/hook": post-delete           # After uninstall
  "helm.sh/hook": pre-rollback          # Before rollback
  "helm.sh/hook": post-rollback         # After rollback
  "helm.sh/hook": test                  # helm test command
  "helm.sh/hook-weight": "-5"           # Order (lower = earlier)
  "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded

DB Migration Hook Job Example

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "mychart.fullname" . }}-migrate
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          command: ["python", "manage.py", "migrate"]
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: {{ .Release.Name }}-db-secret
                  key: url
  backoffLimit: 3

Helm Tests

# Run tests for a release
helm test my-release -n my-ns
helm test my-release -n my-ns --logs

# Test Pod template (place in templates/tests/)
apiVersion: v1
kind: Pod
metadata:
  name: {{ include "mychart.fullname" . }}-test-connection
  annotations:
    "helm.sh/hook": test
spec:
  restartPolicy: Never
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args:
        - '--spider'
        - 'http://{{ include "mychart.fullname" . }}:{{ .Values.service.port }}/healthz'

Common Helm Patterns

Subcharts & Dependencies

# Download dependencies declared in Chart.yaml
helm dependency update ./mychart
helm dependency build ./mychart    # use Chart.lock (reproducible)
helm dependency list ./mychart

# Override subchart values in parent values.yaml:
# values.yaml
postgresql:
  enabled: true
  auth:
    database: myapp
    username: myuser
  primary:
    persistence:
      size: 20Gi

redis:
  enabled: true
  architecture: standalone

Library Charts

# Chart.yaml for a library chart
apiVersion: v2
name: mylib
type: library        # Cannot be installed directly
version: 0.1.0

# Use in _helpers.tpl of consumer chart:
{{- define "mylib.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

Helmfile Usage

# helmfile.yaml example
repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami
  - name: ingress-nginx
    url: https://kubernetes.github.io/ingress-nginx

releases:
  - name: ingress-nginx
    namespace: ingress-nginx
    createNamespace: true
    chart: ingress-nginx/ingress-nginx
    version: 4.9.0
    values:
      - ./values/ingress-nginx.yaml

  - name: myapp
    namespace: production
    chart: ./charts/myapp
    version: 1.2.3
    values:
      - ./values/myapp-base.yaml
      - ./values/myapp-prod.yaml
    secrets:
      - ./secrets/myapp-prod.yaml   # helm-secrets integration

# Common helmfile commands
helmfile sync              # Apply all releases
helmfile diff              # Show pending changes
helmfile apply             # Sync only changed releases
helmfile destroy           # Uninstall all releases
helmfile -l name=myapp sync  # Target specific release

Production Tips

Tip — Immutable Image Tags: Always pin image.tag to a specific SHA or semver tag in production. Never use latest — it makes rollbacks impossible and breaks reproducibility.
# In CI/CD pipeline, set tag to git commit SHA
IMAGE_TAG=$(git rev-parse --short HEAD)
helm upgrade --install myapp ./chart \
  --set image.tag=${IMAGE_TAG} \
  --atomic --timeout 5m \
  --wait

Resource Limits in Charts

# Always provide defaults in values.yaml
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 128Mi

# In templates/deployment.yaml:
resources:
  {{- toYaml .Values.resources | nindent 10 }}

Values Validation with JSON Schema

# Create values.schema.json in chart root
{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["image", "replicaCount"],
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "image": {
      "type": "object",
      "required": ["repository", "tag"],
      "properties": {
        "repository": { "type": "string" },
        "tag": { "type": "string" },
        "pullPolicy": {
          "type": "string",
          "enum": ["Always", "IfNotPresent", "Never"]
        }
      }
    }
  }
}
# Helm validates values against schema on install/upgrade
Warning: Avoid helm upgrade without --atomic in production pipelines. Without it, a failed upgrade leaves the release in a broken state. Use --atomic --timeout 5m to auto-rollback on failure.

Back to Documents