Helm and Kustomize
Installing, upgrading and rolling back Helm releases with values, inspecting charts and releases, and building Kustomize bases and overlays you apply with kubectl apply -k.
Exam tasks: 1.6 (use Helm and Kustomize to install cluster components)
The decision: is this a packaged component someone else publishes with tunable values (Helm), or your own plain manifests that need per-environment changes without templates (Kustomize)?
Which tool
| Helm | Kustomize | |
|---|---|---|
| Unit | Chart: templates plus values.yaml, versioned, from a repo or OCI registry | Kustomization: a folder with kustomization.yaml listing plain manifests |
| Customise by | Values (--set, -f values.yaml) fed into Go templates | Patches, generators and transformers on real YAML |
| State in the cluster | A release with numbered revisions, stored as Secrets in the release namespace | None. It renders YAML; kubectl apply does the rest |
| Rollback | helm rollback to a revision | Re-apply an earlier commit |
| Built into kubectl | No, separate helm binary | Yes: kubectl apply -k, kubectl kustomize |
Helm in practice
helm repo add stellar https://charts.stellar.example
helm repo update
helm search repo stellar/ # charts and versions in that repo
helm show values stellar/metrics-relay > /tmp/relay-values.yaml # what you can set
helm install relay stellar/metrics-relay \
-n telemetry --create-namespace \
--version 2.4.1 \
--set replicaCount=2 \
--set service.type=NodePort \
-f /tmp/relay-overrides.yaml
helm list -n telemetry # or -A for all namespaces
helm status relay -n telemetry
helm get values relay -n telemetry # only the values you supplied; add --all for everything
helm get manifest relay -n telemetry # rendered YAML that was applied| Command | Use |
|---|---|
helm upgrade relay stellar/metrics-relay -n telemetry --reuse-values --set image.tag=2.5.0 | Change one value, keep the rest |
helm upgrade --install ... | Install if missing, upgrade if present. Safe to rerun |
helm history relay -n telemetry | Revisions with status and chart version |
helm rollback relay 2 -n telemetry | Back to revision 2 (creates a new revision) |
helm uninstall relay -n telemetry | Remove the release and its objects |
helm template relay stellar/metrics-relay -f vals.yaml | Render locally, nothing installed |
helm pull stellar/metrics-relay --version 2.4.1 --untar | Download and unpack to inspect or edit |
helm install relay oci://registry.example/charts/metrics-relay --version 2.4.1 | Install straight from an OCI registry |
- Values precedence: chart defaults, then each
-ffile in order, then--setflags. The last one wins. - Releases are namespaced.
helm listwithout-nor-Ashows only the current namespace, which is the usual reason a release "doesn't exist". - Without
--reuse-values,helm upgraderesets every value to the chart defaults plus what you pass this time.
Exam signal
Tasks say things like "install chart X version Y as release Z in namespace N with value V". Map each part to a
flag: release name, chart, --version, -n (plus --create-namespace), --set. Then check with helm list -n N
and helm get values Z -n N.
Values keys that don't exist
--set replicas=3 when the chart's key is replicaCount installs fine and changes nothing: Helm doesn't reject
unknown keys. Always read helm show values for the exact key path, such as controller.service.type.
A chart's layout
metrics-relay/
Chart.yaml # name, version (chart), appVersion (software), dependencies
values.yaml # defaults
templates/ # Go templates, e.g. {{ .Values.replicaCount }}, {{ .Release.Name }}
charts/ # packaged dependenciesLegacy: use Helm 4 flag names and server-side apply instead
Helm 4 (November 2025) renamed --atomic to --rollback-on-failure and --force to --force-replace; the old
names still work with a deprecation warning. New releases install with server-side apply by default. Helm 2's
in-cluster Tiller has been gone since Helm 3; anything that mentions helm init or Tiller is out of date.
Kustomize in practice
relay-kust/
base/
kustomization.yaml
deployment.yaml
service.yaml
overlays/
staging/kustomization.yaml
prod/
kustomization.yaml
replicas-patch.yaml# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
labels:
- pairs:
app.kubernetes.io/name: relay
includeSelectors: true# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: relay-prod
namePrefix: prod-
resources:
- ../../base
images:
- name: relay # image name used in the base
newName: registry.example/relay
newTag: "3.1.0"
replicas:
- name: relay
count: 4
configMapGenerator:
- name: relay-config
literals:
- LOG_LEVEL=warn
patches:
- path: replicas-patch.yaml # strategic merge patch; target found by kind and name
- target:
kind: Deployment
name: relay
patch: |-
- op: add
path: /spec/template/spec/containers/0/env/-
value: {name: REGION, value: eu-1}kubectl kustomize overlays/prod # render, check before applying
kubectl diff -k overlays/prod # what would change
kubectl apply -k overlays/prod
kubectl delete -k overlays/prod| Field | Effect |
|---|---|
resources | Files, folders or other kustomizations to include |
namespace, namePrefix, nameSuffix | Set on every object; references (for example a Service's ConfigMap) are updated too |
labels | Add labels; includeSelectors: true also adds them to selectors |
images | Change image name or tag without editing the Deployment |
configMapGenerator, secretGenerator | Create objects with a content-hash suffix, so Pods roll when data changes |
patches | Strategic merge or JSON 6902 patches, optionally aimed with target |
Generated names and hash suffixes
configMapGenerator names the ConfigMap relay-config-<hash>. References inside the same kustomization are
rewritten automatically, but a kubectl get cm relay-config fails, and any manifest outside the kustomization
that refers to the plain name won't find it. Use generatorOptions: {disableNameSuffixHash: true} only if the
task needs a fixed name.
Legacy: use labels and patches instead
commonLabels, patchesStrategicMerge and patchesJson6902 are deprecated in Kustomize v5. Use labels (set
includeSelectors: true to match the old commonLabels behaviour) and the single patches list.
kustomize edit fix converts an old file.
Scenarios
Each upgrade computes values from the chart defaults and what you pass now, so earlier --set values were
dropped. Pass --reuse-values or supply the full values again with -f. The release name and stored values
aren't tied to repo updates.
Kustomize is built into kubectl and is designed for exactly this: one base, small overlays. Helm needs the helm
binary and a chart. Running set image after every apply drifts from the files, and Deployment fields can't read
environment variables.
Drill
kubectl config use-context cka-pkg
- Using the Helm repo
https://charts.lumen.example(add it aslumen), install chartlumen/edge-proxyversion1.8.0as releaseedgein namespaceingress-edge(create it), withcontroller.replicaCount=2. - In
/opt/kust/relay, a base exists inbase/. Create an overlayoverlays/qathat puts everything in namespacerelay-qawith name prefixqa-and sets the imagerelayto tag3.2.0. Apply it.
helm repo add lumen https://charts.lumen.example && helm repo update
helm show values lumen/edge-proxy --version 1.8.0 | grep -n replicaCount # confirm the key
helm install edge lumen/edge-proxy --version 1.8.0 \
-n ingress-edge --create-namespace --set controller.replicaCount=2
helm list -n ingress-edge
helm get values edge -n ingress-edgemkdir -p /opt/kust/relay/overlays/qa
cat > /opt/kust/relay/overlays/qa/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: relay-qa
namePrefix: qa-
resources:
- ../../base
images:
- name: relay
newTag: "3.2.0"
EOF
k create ns relay-qa
k kustomize /opt/kust/relay/overlays/qa | grep -E 'name:|namespace:|image:'
k apply -k /opt/kust/relay/overlays/qaVerify:
k get deploy -n ingress-edge # 2/2 replicas
k get all -n relay-qa # names start with qa-
k get deploy -n relay-qa -o jsonpath='{..image}' # ends in :3.2.0Further reading
Highly available control plane
Stacked vs external etcd, quorum and fault tolerance, the load-balanced control plane endpoint, joining extra control plane nodes with upload-certs and certificate keys, and leader election.
Extension interfaces (CRI, CNI, CSI)
What the kubelet delegates to container runtimes, network plugins and storage drivers, where each one is configured on a node, and how to tell which plugin is installed.