In a helm create chart, why does the image tag default to .Chart.AppVersion?
answer
- An empty string is not nothing here
- The fallback reads chart metadata, not values
- Falsy values are what default keys on
- Metadata bump becomes a content change
- Nothing checks the tag against a registry
basics
~20 sSo the chart installs unedited and ships a matching image: values.yaml sets image.tag to an empty string, which is falsy, so the default function falls back to the chart's appVersion. It couples the published appVersion to what an unedited install runs.
solid answer
~40 sThe scaffold pairs `tag: ""` in `values.yaml` with `{{ .Values.image.tag | default .Chart.AppVersion }}` in the Deployment. `default` treats an empty string as absent, so an untouched install renders the image tag from `Chart.yaml`'s `appVersion`, and a consumer who sets `image.tag` overrides it. The benefit is that one chart version identifies one application build, so `helm install` with no values does something sensible. The cost is coupling: bumping `appVersion` changes what an unedited install deploys, so it must always ship with a chart `version` bump too. Nothing checks that the resulting tag exists in the registry — a typo in `appVersion` becomes an image-pull failure at rollout, not a render error.
code
yaml · 5 linesimage:
repository: registry.example.com/invoice-worker
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""go deeper
Know where the image tag comes from when you install a scaffolded chart without setting anything: the appVersion field in Chart.yaml, reached through the default function. Know that setting image.tag overrides it.
Explain the mechanism precisely — default treats an empty string as absent, so tag: "" is a deliberate falsy placeholder — and say why the fallback reads chart metadata rather than a value.
Show the consequence: a metadata field now affects rendered output, so appVersion never moves without a chart version bump, and a bad tag surfaces as a failed pull long after everything green.
Weigh the tradeoff for charts your organisation publishes: a self-installing default versus an explicit required tag, and who owns keeping appVersion and the pushed image in step.
## The two halves of the pattern `helm create` scaffolds a chart whose image reference is assembled from values with a fallback: ```yaml # values.yaml image: repository: registry.example.com/invoice-worker pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" ``` ```yaml # templates/deployment.yaml (fragment) image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" ``` `default` is a template function that returns its first argument when the piped value is *empty* — and empty means any zero value: an empty string, `0`, `false`, `nil`, an empty list or map. The scaffold deliberately declares `tag: ""` rather than omitting the key, so the key is documented and discoverable in `values.yaml` while still being falsy, which is what makes the fallback fire. ## What the default buys you A chart that installs correctly with no values at all is a chart people can try. Because the fallback reads `.Chart.AppVersion`, chart `invoice-worker-2.96.0` with `appVersion: "1.14.3"` deploys image tag `1.14.3` for anyone who does not care to choose. One chart version then identifies one application build, and `helm list` reports both numbers side by side, so an operator can tell which build a release is running without reading any values. It also gives the consumer a clean override: setting `image.tag` on the command line or in a values file pins a hotfix build without waiting for a new chart to be published. ## What the default couples The cost is a real dependency between a metadata field and rendered output. Three consequences follow. **A bare `appVersion` bump is a content change.** Since the rendered image tag reads that field, editing `appVersion` alone changes what an unedited install produces. If you publish that without bumping the chart `version`, two people who both installed `invoice-worker-2.96.0` get different images. Any `appVersion` change ships with a chart `version` bump — no exceptions. **Nothing validates the tag.** `appVersion` is a free-form string that Helm never checks against a registry. Rendering, linting and the dry run all succeed with a typo; the failure appears when the cluster tries to pull the image and the pod never starts. It is a slow, confusing failure precisely because everything upstream of it looked green. **Quoting matters twice.** `Chart.yaml` is YAML, so an unquoted `appVersion: 1.10` is a number worth 1.1, and the rendered tag follows. The same trap sits in the values file: `--set image.tag=1.10` or an unquoted `tag: 1.10` produces a numeric value, which is why the template concatenates into a quoted string. There is a subtler consequence to know about: the scaffold also derives an `app.kubernetes.io/version` label from `.Chart.AppVersion`. A consumer who overrides `image.tag` therefore runs one build while the object is labelled with another. That is a legitimate reason for a chart author to prefer that consumers *not* override the tag routinely, and to publish a new chart version for every application build instead. ## When to keep it and when to drop it Keep the fallback when the chart and the application ship together and the chart is the delivery vehicle for that application. It makes the chart self-describing and installable by anyone. Drop it — that is, make `image.tag` a required value — when the chart is a generic wrapper deployed against images the chart author does not publish. Then there is no honest default, and failing loudly at render time is better than rendering a tag nobody meant: ```yaml image: "{{ .Values.image.repository }}:{{ required "image.tag is required" .Values.image.tag }}" ``` `required` is one of the functions Helm adds on top of the standard template library; it aborts the render with your message when the value is absent or empty, turning a silent mis-deploy into a failed `helm upgrade` that never reaches the cluster. ## In an interview The expected answer names the mechanism (`default` plus a falsy `tag: ""`), names the field it falls back to (`.Chart.AppVersion`, not `.Values`), and then shows judgement: this is a coupling, it makes the two `Chart.yaml` numbers move together on every application release, and it fails at pull time rather than render time when the tag is wrong.
- Why does the scaffold write tag: "" instead of leaving the key out of values.yaml?Two reasons. The key stays visible and documented in `values.yaml`, so a consumer reading the file knows it exists and what it overrides. And an empty string is a zero value, which is exactly what `default` treats as absent — so the declared key still lets the `.Chart.AppVersion` fallback fire. Omitting the key would behave the same at render time but would hide the knob.
- A chart bumps appVersion from 1.14.3 to 1.14.4 and republishes under the same chart version. What breaks?Chart version 2.96.0 now renders a different image than it did yesterday. Anyone who pinned that version and reinstalls or re-renders gets 1.14.4 without asking, and two clusters believed to be identical are not. The chart version is the only identifier consumers hold, so any change to rendered output — including one caused by metadata — requires a new chart version.
- How would you make the chart fail early rather than deploy a tag that does not exist?Helm cannot check a registry, so the earliest honest failure is at render time. Replace the silent fallback with `required` when there is no trustworthy default, and validate the shape of `image.tag` in the chart's values schema. Beyond that it is a publishing-pipeline concern: the job that stamps `appVersion` should be the same one that pushed the image, so the two cannot disagree.
saying these in an interview costs you the question
- Saying the tag defaults to latest
- Thinking default fires only on a missing key
- Believing Helm verifies the tag exists in the registry
- Calling it .Values.appVersion instead of .Chart.AppVersion
- Bumping appVersion without a chart version bump
- Leaving a numeric-looking tag unquoted in values