Helm Charts
CKA prep • The Kubernetes package manager: charts, templates/values, releases, repos
Key terms
| Term | Meaning |
|---|---|
| Helm | The package manager for Kubernetes |
| Chart | A versioned bundle of templated manifests |
| Values | The configuration that fills in the templates (values.yaml) |
| Template | A manifest with Go template placeholders ({{ }}) |
| Release | A named, installed instance of a chart in a cluster |
| Repository | An HTTP(S) index that serves packaged charts |
| Revision | Each install/upgrade bumps a release revision (for rollback) |
Problem & solution
Real apps are many manifests — Deployment, Service, ConfigMap, Ingress, HPA — and you copy-paste them per environment, changing a few values each time. That is error-prone and unversioned. Helm packages all of it into one templated, versioned chart, parameterised by values, and tracks each install as a release you can upgrade and roll back.
Solution: Package manifests as a templated chart, override per-environment values at install time, and manage the lifecycle (install/upgrade/rollback) as versioned releases stored in-cluster.
The analogy
Buying furniture as a flat-pack kit means the parts and the assembly steps are standardized, you just supply one parameter sheet per room, the wood color here, five chairs there, and the same kit builds a different finished room each time. You order the kit from a furniture catalog, fill in the sheet, and end up with a specific assembled set in that room that you can later modify or take apart. Helm works the same way: a chart is the kit of templated manifests, values is the parameter sheet you fill per environment, a release is one installed, running instance, and a repository is the catalog you pull charts from.
Helm 3 architecture (no Tiller)
Helm 3 dropped the in-cluster Tiller server. The CLI renders client-side and talks to the API directly with your RBAC; release history lives in Secrets.
Chart directory structure
Every chart follows the same layout, so once you know it you can navigate any chart. Here is what each file and folder is for:
helm create mychart # scaffold a working chart
helm lint mychart # validate structure + templates
mychart/ ├── Chart.yaml # name, version, appVersion, dependencies ├── values.yaml # default configuration values ├── charts/ # vendored sub-chart dependencies ├── templates/ # templated manifests │ ├── deployment.yaml │ ├── service.yaml │ ├── _helpers.tpl # reusable template snippets │ └── NOTES.txt # post-install message └── .helmignore
Templates and values
Templates are manifests with Go template directives; .Values comes from
values.yaml (and any override), .Release/.Chart are built-in objects.
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.port }}
# values.yaml
replicaCount: 2
image:
repository: nginx
tag: "1.27"
service:
port: 80
Render and inspect before applying
Always preview what a chart will produce before it touches the cluster. These commands render or server-validate without installing anything:
helm template mychart # render to stdout, apply nothing
helm template mychart --set replicaCount=5 # see an override applied
helm install web ./mychart --dry-run --debug # server-validate without installing
Release lifecycle: install, upgrade, rollback
These are the everyday commands for the full life of a release, from first install through upgrades and rollback to removal:
# install a named release (override values inline or with a file)
helm install web ./mychart --set image.tag=1.27 -n prod --create-namespace
helm install web ./mychart -f prod-values.yaml -n prod
# list and inspect releases
helm list -n prod
helm status web -n prod
helm get values web -n prod
# upgrade (idempotent install+upgrade with one command)
helm upgrade --install web ./mychart --set replicaCount=4 -n prod
# history and rollback to a previous revision
helm history web -n prod
helm rollback web 1 -n prod
# remove the release (keep history with --keep-history)
helm uninstall web -n prod
Repositories and dependencies
Charts are shared through repositories, much like OS packages. These commands add a repo, find charts, and publish your own:
# add a public repo, refresh its index, search, and install
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo nginx
helm install store bitnami/nginx -n web --create-namespace
# package + push your own chart to a repo (or an OCI registry)
helm package mychart # -> mychart-0.1.0.tgz
helm push mychart-0.1.0.tgz oci://registry.example.com/charts
Declare chart dependencies in Chart.yaml and pull them with
helm dependency update, which populates charts/.
End-to-end: from chart to running release
End-to-end example: scaffold, install, upgrade, and roll back a chart
A full lifecycle: scaffold a chart, trim it to a Deployment + Service, install
revision 1, upgrade to revision 2 with --set, break it, then roll back to a
known-good state and confirm the resulting revision history.
- Scaffold and lint a fresh chart:
helm create web
helm lint web
# expected: 1 chart(s) linted, 0 chart(s) failed
- Replace
web/values.yamldefaults with a minimal, explicit set:
# web/values.yaml
replicaCount: 2
image:
repository: nginx
tag: "1.27"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
- Point the generated
web/templates/deployment.yamlcontainer at those values (the key lines):
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.port }}
- Preview the rendered YAML before touching the cluster:
helm template web ./web --set replicaCount=3 | grep -E "replicas|image:"
# expected: replicas: 3 and image: "nginx:1.27"
- Install revision 1 into its own namespace:
helm install web ./web -n web --create-namespace
kubectl -n web get deploy web -o jsonpath='{.spec.replicas}{"\n"}' # 2
helm list -n web
# expected: web web 1 ... deployed web-0.1.0
- Upgrade to revision 2, scaling out and bumping the image:
helm upgrade --install web ./web -n web \
--set replicaCount=4 --set image.tag=1.27.2
kubectl -n web get deploy web -o jsonpath='{.spec.replicas}{"\n"}' # 4
helm history web -n web
# expected: revision 1 superseded, revision 2 deployed
- Push a bad upgrade (nonexistent tag), watch it fail to roll out, then roll back:
helm upgrade web ./web -n web --set image.tag=does-not-exist
kubectl -n web rollout status deploy/web --timeout=30s || true
# expected: pods stuck ImagePullBackOff
helm rollback web 2 -n web
kubectl -n web rollout status deploy/web
# expected: deployment "web" successfully rolled out
- Confirm the rollback created a NEW revision that restores revision 2's state:
helm history web -n web
# expected: revision 4 deployed, description "Rollback to 2"
kubectl -n web get deploy web -o jsonpath='{..image}{"\n"}' # nginx:1.27.2
Common pitfalls
These are the common Helm mistakes and what each symptom usually means:
- YAML indentation in templates -> use nindent/indent, e.g. {{ toYaml .Values.x | nindent 8 }}
- changed values, nothing moved -> you ran `template`, not `upgrade`/`install`
- release name collision -> names are unique per namespace
- secrets in values.yaml in git -> keep secret values out of source control
- rollback didn't help -> rollback creates a NEW revision; check history
- CRDs not upgraded -> Helm does not upgrade CRDs from crds/ automatically
Key takeaways
- A chart = templated manifests + default values; an install = a release.
- Helm 3 is client-side (no Tiller); release state lives in Secrets.
helm template/--dry-runlets you preview rendered YAML safely.helm upgrade --installis the idempotent workhorse; revisions enable rollback.helm rollbackmakes a new revision that restores an old one.- Add repos (or OCI registries) to install and share charts.
Checklist
- [ ] Scaffolded a chart with
helm createand read the structure - [ ] Edited templates to consume
.Valuesand rendered withhelm template - [ ] Installed a release and overrode values with
--setand-f - [ ] Upgraded the release and rolled it back to a prior revision
- [ ] Added a repo, searched, and installed a third-party chart