Skip to main content

Helm: The Kubernetes Package Manager

Helm is the industry-standard tool for managing Kubernetes application lifecycles. It moves beyond raw YAML hydration (Kustomize) by providing a Stateful Release Engine capable of versioning, rollbacks, and complex logical templating.


1. ARCHITECTURE: THE TILLER-LESS MODEL

In Helm v2, a server-side component called "Tiller" was required. Helm v3 removed Tiller, adopting a Client-Only Architecture that relies on the user's kubeconfig and RBAC.

1.1 The Release Backend (Where is the state?)

Helm does not store its state in a database. Instead, it uses Kubernetes Secrets (by default) or ConfigMaps within the namespace of the release.

  • Storage Logic: For every helm install or helm upgrade, a new Secret is created: sh.helm.release.v1.[RELEASE_NAME].v[REVISION].
  • Rollback Mechanism: When you run helm rollback, Helm simply fetches the manifest from a previous Secret revision and applies it back to the cluster.

1.2 The Rendering Engine

Helm uses the Go text/template library combined with the Sprig function library.

  1. Collect: Gathers values from values.yaml, --values, and --set.
  2. Render: Injects values into the templates/*.yaml files.
  3. Validate: Parses the resulting YAML to ensure it is valid Kubernetes schema.
  4. Submit: Calls the Kubernetes API to reconcile the resources.

2. THE ANATOMY OF A CHART

A "Bible-grade" Chart is modular and follows the Don't Repeat Yourself (DRY) principle.

my-app/
├── Chart.yaml # Metadata: Version (Chart), AppVersion (Image)
├── values.yaml # Default overrides
├── templates/
│ ├── _helpers.tpl # Named templates (reusable snippets)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── NOTES.txt # Post-installation CLI instructions
│ └── tests/ # helm test logic
└── charts/ # Sub-charts (Dependencies)

2.1 Chart.yaml (Versioning Strategy)

  • version: The version of the Helm Chart (Increment this for YAML changes).
  • appVersion: The version of the application/image (Increment this for code changes).

3. ADVANCED TEMPLATING (The Ninja Toolkit)

3.1 Named Templates (_helpers.tpl)

Use define to create reusable blocks and include to inject them.

{{/* _helpers.tpl */}}
{{- define "my-app.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
managed-by: {{ .Release.Service }}
{{- end }}

Implementation in Deployment:

metadata:
labels:
{{- include "my-app.labels" . | nindent 4 }}

3.2 Powerful Functions & Pipelines

  • quote: {{ .Values.name | quote }} -> "value"
  • default: {{ .Values.port | default 8080 }}
  • toYaml: Essential for injecting nested blocks from values.yaml.
    resources:
    {{- toYaml .Values.resources | nindent 6 }}

4. LIFECYCLE HOOKS (Production Workflow)

Hooks allow you to execute code at specific points in the release lifecycle (e.g., running a database migration before an upgrade).

HookPurpose
pre-installRun before templates are rendered.
post-installRun after all resources are created.
pre-upgradeCrucial: Run database migrations before new Pods start.
pre-rollbackCleanup before reverting state.

Example Hook (Database Migration):

apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-db-migrate
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: migrate
image: my-migrator:latest
restartPolicy: Never

5. REPOSITORY & RELEASE MANAGEMENT

5.1 The "Set" vs. "Values" Precedence

Values are merged in this order (highest priority wins):

  1. Individual parameters: --set key=value
  2. Environment values file: -f values-prod.yaml
  3. Default Chart file: values.yaml

5.2 Production Command Cheat Sheet

# 1. Inspect the state of a release
helm get manifest my-release

# 2. Audit the history and find a revision for rollback
helm history my-release

# 3. Dry-run with debug to see the final YAML before applying
helm upgrade --install my-release ./my-chart --dry-run --debug

# 4. Atomic Upgrades: If it fails, auto-rollback
helm upgrade --install my-release ./my-chart --atomic --timeout 5m

6. MULTI-ENVIRONMENT ARCHITECTURE (The Bible Pattern)

For production, do not edit values.yaml. Keep it for defaults and use external files for environmental differences.

# Dev Deployment
helm upgrade --install my-api ./chart -f env/dev.yaml --namespace dev

# Prod Deployment (Different Replicas, Ingress Host, and Secrets)
helm upgrade --install my-api ./chart -f env/prod.yaml --namespace prod --wait --atomic

env/prod.yaml Example:

replicaCount: 10
ingress:
host: api.production.com
resources:
limits:
memory: 4Gi

7. PRODUCTION PITFALLS & REAL-WORLD WARNINGS

  1. The "Release Name" Constraint: Release names must be unique per namespace.
  2. Secret Limits: By default, Helm keeps 10 revisions in history. In clusters with thousands of releases, this can bloat the etcd database.
    • Fix: Set --history-max 5 during installation.
  3. Missing tpl Function: If you want to use Helm variables inside a string stored in values.yaml, you must use the tpl function.
    • Example: {{ tpl .Values.myConfigString . }}
  4. Chart Version vs App Version: Never confuse these. If you change a resource limit in YAML, increment the Chart version. If you change the code, increment the AppVersion.

8. VISUAL: HELM V3 DATA FLOW


9. NINJA AUDITING COMMANDS

View raw state of a release in Etcd

# Decode the base64/gzip release data stored in the K8s Secret
kubectl get secret -n my-ns sh.helm.release.v1.my-app.v1 -o jsonpath='{.data.release}' | base64 -d | base64 -d | gzip -d | jq

Dependency Management

If your chart relies on others (e.g., a Redis sub-chart):

# 1. Update your local dependencies
helm dependency update ./my-chart

# 2. Check the build lock
cat ./my-chart/Chart.lock