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 installorhelm 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.
- Collect: Gathers values from
values.yaml,--values, and--set. - Render: Injects values into the
templates/*.yamlfiles. - Validate: Parses the resulting YAML to ensure it is valid Kubernetes schema.
- 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 fromvalues.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).
| Hook | Purpose |
|---|---|
pre-install | Run before templates are rendered. |
post-install | Run after all resources are created. |
pre-upgrade | Crucial: Run database migrations before new Pods start. |
pre-rollback | Cleanup 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):
- Individual parameters:
--set key=value - Environment values file:
-f values-prod.yaml - 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
- The "Release Name" Constraint: Release names must be unique per namespace.
- Secret Limits: By default, Helm keeps 10 revisions in history. In clusters with thousands of releases, this can bloat the
etcddatabase.- Fix: Set
--history-max 5during installation.
- Fix: Set
- Missing
tplFunction: If you want to use Helm variables inside a string stored invalues.yaml, you must use thetplfunction.- Example:
{{ tpl .Values.myConfigString . }}
- Example:
- 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