Asterrr's Handbook

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

HelmKustomize
UnitChart: templates plus values.yaml, versioned, from a repo or OCI registryKustomization: a folder with kustomization.yaml listing plain manifests
Customise byValues (--set, -f values.yaml) fed into Go templatesPatches, generators and transformers on real YAML
State in the clusterA release with numbered revisions, stored as Secrets in the release namespaceNone. It renders YAML; kubectl apply does the rest
Rollbackhelm rollback to a revisionRe-apply an earlier commit
Built into kubectlNo, separate helm binaryYes: 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
CommandUse
helm upgrade relay stellar/metrics-relay -n telemetry --reuse-values --set image.tag=2.5.0Change one value, keep the rest
helm upgrade --install ...Install if missing, upgrade if present. Safe to rerun
helm history relay -n telemetryRevisions with status and chart version
helm rollback relay 2 -n telemetryBack to revision 2 (creates a new revision)
helm uninstall relay -n telemetryRemove the release and its objects
helm template relay stellar/metrics-relay -f vals.yamlRender locally, nothing installed
helm pull stellar/metrics-relay --version 2.4.1 --untarDownload and unpack to inspect or edit
helm install relay oci://registry.example/charts/metrics-relay --version 2.4.1Install straight from an OCI registry
  • Values precedence: chart defaults, then each -f file in order, then --set flags. The last one wins.
  • Releases are namespaced. helm list without -n or -A shows only the current namespace, which is the usual reason a release "doesn't exist".
  • Without --reuse-values, helm upgrade resets 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 dependencies

Legacy: 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
FieldEffect
resourcesFiles, folders or other kustomizations to include
namespace, namePrefix, nameSuffixSet on every object; references (for example a Service's ConfigMap) are updated too
labelsAdd labels; includeSelectors: true also adds them to selectors
imagesChange image name or tag without editing the Deployment
configMapGenerator, secretGeneratorCreate objects with a content-hash suffix, so Pods roll when data changes
patchesStrategic 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

Scenario
A release named gate was installed with `--set controller.replicaCount=3 --set controller.service.type=NodePort`. You run `helm upgrade gate acme/gate -n edge --set image.tag=1.9.2`. Afterwards the controller has 1 replica and a LoadBalancer Service. Why?
Scenario
A team keeps one base for its API and needs staging to run 1 replica with tag 3.0.0-rc2, and prod to run 4 replicas with tag 3.0.0, without copying the Deployment manifest. Which approach fits with only kubectl available?

Drill

kubectl config use-context cka-pkg

  1. Using the Helm repo https://charts.lumen.example (add it as lumen), install chart lumen/edge-proxy version 1.8.0 as release edge in namespace ingress-edge (create it), with controller.replicaCount=2.
  2. In /opt/kust/relay, a base exists in base/. Create an overlay overlays/qa that puts everything in namespace relay-qa with name prefix qa- and sets the image relay to tag 3.2.0. Apply it.

Further reading

On this page