skip to content

Which Helm built-in objects are safe to build a Kubernetes resource name from?

level: seniorimportance: should knowfreq 45%

answer

  1. Identity, not a rename operation
  2. Two of the built-ins never move
  3. Version fields move on every publish
  4. A new name means create plus delete
  5. Moving values belong in labels and annotations

basics

~20 s

Only the ones that do not change between upgrades: .Release.Name, .Chart.Name and stable values. .Chart.Version, .Chart.AppVersion, .Release.Revision and .Release.IsInstall all move, and a moved name makes Helm create a new object and delete the old one.

solid answer

~50 s

A name is an identity, so it must be built only from things that are constant for the life of the release. `.Release.Name` is fixed at install. `.Chart.Name` changes only if someone renames the chart. Those two, plus a value the caller sets once, are the safe set - and they are exactly what the generated fullname helper uses. Everything else on the built-in objects moves: `.Chart.Version` and `.Chart.AppVersion` change on every release of the chart, `.Release.Revision` changes on every upgrade, `IsInstall`/`IsUpgrade` differ between operations, and sprig's `now` or a random string differ on every render. When a name changes, Helm does not rename anything: the new name is a new object to create, and the old name is missing from the new manifest, so it is deleted. For a Service that means a new cluster IP; for a claim it means the old volume is orphaned. Put moving values in labels and annotations instead.

code

yaml · 14 lines
yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-{{ .Chart.Name }}
  labels:
    app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
    helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
spec:
  selector:
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/name: {{ .Chart.Name }}
  ports:
    - port: 8080
      targetPort: http

go deeper

for a junior

Remember the safe pair: .Release.Name and .Chart.Name. Anything with the word version in it, or the revision number, does not belong in a name - put it in a label.

for a middle

Explain the mechanism: Helm matches objects by kind, namespace and name, so a changed name is a create plus a delete rather than a rename. Know which fields move and how often.

for a senior

Walk an interviewer through the blast radius per kind - claims lose their volume, Services get a new cluster IP and DNS name, ServiceAccounts break trust - and show how you would catch it by diffing two rendered chart versions before upgrading.

for a principal

Own the naming convention across a chart estate: what components a name may contain, how a rename is handled when one is genuinely required, and how you review chart changes so a helper edit does not silently replace resources across dozens of services.

### Why the question is about identity Helm does not diff objects by position or by some internal id. On upgrade it compares the manifest stored with the previous release against the manifest it just rendered, keyed by object identity - kind, namespace and **name**. Anything in the new manifest whose identity is new gets created; anything in the old manifest whose identity has disappeared gets deleted. There is no rename operation anywhere in that model. So the question "which built-ins are safe in a name?" is really "which built-ins are constant for the life of this release?" ### The stable set **`.Release.Name`** is chosen at install and is fixed for the release's life. Upgrading never changes it - you cannot change a release's name, you can only uninstall and install another. It is the single most reliable component of a name. **`.Chart.Name`** comes from `Chart.yaml` and changes only when someone deliberately renames the chart, which is itself a breaking change everyone understands. Note that inside a subchart's templates it is the *subchart's* name, which is what makes `{{ .Release.Name }}-{{ .Chart.Name }}` unique per component in a multi-chart install without any manual prefixing. **A value the caller sets** - a `nameOverride`, a component suffix like `worker` or `api` - is stable as long as the caller keeps passing it. That is a social guarantee rather than a mechanical one, and it is why charts that let a name be overridden usually warn that changing it later replaces resources. This is exactly the recipe the scaffolded fullname helper uses: release name plus chart name, with a `nameOverride`/`fullnameOverride` escape hatch, truncated to 63 characters and stripped of a trailing dash because several Kubernetes object names are limited to that length. ### The moving set **`.Chart.Version`** changes on every chart release - that is what a chart version is for. A name containing it is a name that changes every time you publish. **`.Chart.AppVersion`** changes on every application release, which is even more often. **`.Release.Revision`** changes on literally every upgrade and rollback. **`.Release.IsInstall` / `.Release.IsUpgrade`** differ between the install render and every later render, so a name built from them is guaranteed to move on the first upgrade. **Anything non-deterministic** - `now`, a random suffix, a hash of the current time - is a different name on every render, which means every upgrade deletes and recreates the object even when nothing changed. Charts that need a generated suffix must either persist it in values or accept that churn deliberately. ### What actually breaks Take a 41-service platform chart, where each service is a subchart and a shared name helper was written as `{{ .Release.Name }}-{{ .Chart.Name }}-{{ .Chart.Version }}`. Someone bumps the payments ledger subchart from 1.4.2 to 1.4.3 to tighten a readiness probe, and the parent chart from 2.14.3 to 2.15.0. Nine of the 41 subcharts had a version bump in that release. The upgrade renders 9 services' worth of objects under new names, so Helm creates 9 new Deployments, Services and ServiceAccounts and deletes the 9 old ones. The consequences are not symmetrical across kinds: - **Service**: the new Service gets a new cluster IP and a new DNS name. Callers that resolved the old name keep sending traffic at an address that is being deleted underneath them. For the payments ledger this showed up as a several-minute window of connection failures from every caller that had the old name cached or hard-coded in its own configuration. - **PersistentVolumeClaim**: the new claim binds new storage. The old claim is deleted, and whether the data survives depends entirely on the reclaim policy behind it. This is the one that loses data. - **ServiceAccount and its bindings**: new identity, so anything that trusted the old subject name stops working until it is updated. - **Deployment**: relatively benign - the new one rolls out, the old one is removed - but you get a full replacement rather than a rolling update, so both exist at once and then one vanishes. The failure is quiet at render time. `helm template` produces perfectly valid YAML; nothing is malformed. It only becomes visible as churn in the diff, which is why reviewing a rendered diff between two chart versions before upgrading catches it and reading the chart source often does not. ### Where the moving values belong They belong in metadata, where changing them costs nothing structural: `helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}` and `app.kubernetes.io/version: {{ .Chart.AppVersion }}` as labels, and `.Release.Revision` as a pod-template annotation when you want an upgrade to force a rollout. Labels and annotations are updated in place on the same object; names are not. One caveat worth stating: putting a changing value into a **pod template's** labels or annotations is deliberately not free - it changes the pod template and therefore rolls the workload. That is usually the point. Putting it on the outer object's metadata does not roll anything.

  • Why does Helm delete the old object instead of renaming it when a name changes?
    Because there is no rename in the model. Helm compares the previous release's stored manifest with the newly rendered one by object identity - kind, namespace, name. A new name is an object that did not exist, so it is created; the old name is an object that has vanished from the manifest, so it is deleted. The two events are unrelated as far as Helm is concerned.
  • Where should .Chart.Version and .Chart.AppVersion go instead?
    Into labels and annotations, which are updated in place on the same object: `helm.sh/chart` for the chart name and version, `app.kubernetes.io/version` for the app version. Just be aware that putting a changing value inside a pod template's labels or annotations changes the pod template and therefore rolls the workload - fine when that is intended, surprising when it is not.
  • A chart appends a random suffix to a name so each install is distinct. What goes wrong?
    The suffix is regenerated on every render, so every upgrade produces a name the previous manifest did not contain: Helm creates a fresh object and deletes the previous one, on every single upgrade, even when nothing else changed. If distinctness per install is genuinely needed, derive it from the release name, or persist the generated value in the release's values so later renders reproduce it.
  • Which resource kinds hurt most when their name moves?
    Claims for persistent storage are worst - the new claim binds new storage and the old one is deleted, so data survival depends entirely on the reclaim policy behind it. Services are next, because a new name means a new cluster IP and a new DNS name for every caller. ServiceAccounts break anything that trusted the old subject. A Deployment is comparatively cheap: you get a replacement rather than a rolling update.

saying these in an interview costs you the question

  • Puts .Chart.Version into a resource name for traceability
  • Believes Helm renames an object when its name changes
  • Thinks a changed name is just a rolling update
  • Uses a random suffix in a name and expects stability
  • Assumes helm template would have flagged the churn
  • Puts .Release.Revision in a name to force a rollout

context