Why promote a packaged, version-pinned Helm chart instead of upgrading from the branch checkout?
answer
- A working tree is not a fixed artifact
- Build the chart once, install it many times
- The tarball carries its resolved subcharts
- Select it with --version, not a path
- Chart version and values move separately
basics
~20 sA branch checkout is not a fixed artifact: each environment renders whatever the branch held when it deployed. Packaging once with helm package and installing that exact chart version everywhere leaves the values file as the only deliberate difference.
solid answer
~40 sPackage the chart once — `helm package` produces `fraud-platform-1.7.3.tgz`, a self-contained tarball whose `charts/` directory already holds the subchart versions `Chart.lock` resolved — and push it to a chart repository or an OCI registry. Every environment then runs `helm upgrade --install fraud-platform oci://registry.example.com/charts/fraud-platform --version 1.7.3 -f values/<env>.yaml`, so the chart is byte-identical and only the values file differs. Deploying from a directory on a branch instead means any commit that lands between the staging deploy and the production deploy silently changes what production renders, and a `helm dependency update` at deploy time can pull different subchart versions. Pinning also makes promotion checkable: `helm list` and `helm get metadata` report which chart version each environment is on, so 'staging is on 1.7.3, prod is on 1.6.9' is a fact rather than a guess.
code
bash · 11 lineshelm dependency update charts/fraud-platform
helm package charts/fraud-platform --version 1.7.3 --app-version 2026.9.1
helm push fraud-platform-1.7.3.tgz oci://registry.example.com/charts
helm upgrade --install fraud-platform \
oci://registry.example.com/charts/fraud-platform --version 1.7.3 \
-n fraud-staging -f values/staging.yaml
helm upgrade --install fraud-platform \
oci://registry.example.com/charts/fraud-platform --version 1.7.3 \
-n fraud-prod -f values/prod.yamlgo deeper
Know that helm package turns a chart directory into a versioned .tgz and that --version on install or upgrade selects a published chart version. Recognise that installing from a path and installing a pinned version are not the same thing.
Explain the difference between version and appVersion in Chart.yaml, what the packaged tarball contains, and how Chart.lock freezes subchart versions at package time rather than at deploy time.
Demonstrate the operating consequence: name the failure where a merge lands between two deploys, show how you audit which chart version each environment runs, and insist on moving chart version and values in separate windows.
Own the artifact policy — where charts are published, whether a published version is immutable, whether the application version travels inside the chart, and what it costs the organisation when staging cannot honestly claim to have run what production is about to.
## What identifies a chart `Chart.yaml` carries two version fields and they answer different questions. `version` is the SemVer of the **chart artifact** — the templates, defaults and dependency set — and it is what `helm package` puts in the tarball name, what a repository indexes, and what `--version` selects. `appVersion` is a free-form string naming the **application** the chart deploys; it is informational, and the scaffolded image line reads it as a fallback tag (`{{ .Values.image.tag | default .Chart.AppVersion }}`). Promotion is about the first of these: moving one chart version across environments. ## Why a directory is not an artifact `helm upgrade fraud-platform ./charts/fraud-platform` is legal and convenient, and it is what makes environment drift invisible. The chart it installs is whatever that working tree contained at that instant. If staging deployed at 09:10 and production at 15:40, every merge in between is in production and was never in staging — not as a values change anyone reviewed, but as template changes nobody attributed to the deploy. Dependencies make it worse: if the deploy job runs `helm dependency update` first, subchart versions are re-resolved against whatever the constraint in `Chart.yaml` allows now, so an umbrella chart bundling a fraud-scoring API and a worker can acquire a new worker subchart on the production deploy alone. ## Package once, promote the same version `helm package charts/fraud-platform` produces `fraud-platform-1.7.3.tgz` — for this chart about 3.4 MB, because `charts/` contains the two subchart tarballs that `helm dependency update` resolved and `Chart.lock` recorded. That tarball is the unit of promotion. Push it once, then install it into each environment by version: ```bash helm upgrade --install fraud-platform \ oci://registry.example.com/charts/fraud-platform --version 1.7.3 \ -n fraud-prod -f values/prod.yaml ``` Now exactly two things vary across environments: the values file, and *when* each environment got the version. That is what makes staging meaningful — staging is not merely similar to production, it ran the same chart. ## Verifying a promotion Because Helm records the chart a release was installed from, promotion is auditable after the fact. `helm list -n fraud-prod` shows a CHART column reading `fraud-platform-1.7.3`, `helm history` shows the chart version at every revision, and `helm get metadata` reports the chart name, version and appVersion for the current release. Comparing those three environments is a five-second check that no amount of pipeline logging replaces. `--version` accepts a constraint as well as an exact tag, and that is worth refusing on a promotion path. `--version '^1.7.0'` resolves at deploy time against whatever the repository holds then, which reintroduces precisely the drift that packaging removed: staging resolves 1.7.3 in the morning and production resolves 1.8.0 in the afternoon. Promotion means an exact version, written down, that a human can read in the deploy log. ## Immutability is not free Helm does not by itself stop a version being republished with different contents: a classic repository is an `index.yaml` pointing at tarballs, and whoever can write the tarball can replace it. Get immutability from the repository or registry that stores the chart, keep the tarball you tested rather than rebuilding it per environment, and if you need cryptographic assurance, `helm package --sign` writes a `.prov` file beside the `.tgz` that can be verified at install. Signing charts is opt-in, so nothing happens unless you ask for it. ## Two axes, one at a time There are two independent things that can move during a deploy: the chart version, and the values. A clean promotion moves the chart version and leaves the environment file alone; a configuration change moves the file and leaves the version alone. Moving both in the same production window is the reason so many incident reviews cannot say whether the chart or the config broke it. Where the application image tag lives is the same question in miniature: if the tag is a per-environment value, promoting the chart does not promote the application, and you have two promotion paths to keep in step. Many teams instead let the chart's `appVersion` supply the default tag, so the application version travels inside the chart artifact and one promotion moves both. ## The practical rule Build the chart once, on merge or on tag; deploy that version to dev; deploy the same version to staging; deploy the same version to production. If any environment is deploying from a path on disk rather than a version from a repository, the word 'promotion' is not describing what is happening.
- What stops a chart version from being rebuilt with different contents under the same number?Nothing in Helm. A classic repository is an index pointing at tarballs, and whoever can write there can replace one. Enforce immutability in the repository or registry, promote the tarball you actually tested rather than rebuilding per environment, and for cryptographic assurance use `helm package --sign`, which writes a clear-signed `.prov` beside the `.tgz` for verification at install time. Chart signing is opt-in and off unless you ask for it.
- Does promoting a chart version also promote the application image?Only if you have arranged it. If the image tag is set in each environment's values file, the chart and the application are two separate promotion paths that can drift. The common alternative is to let the chart's `appVersion` supply the default tag — the scaffolded template reads `.Values.image.tag | default .Chart.AppVersion` — so the application version travels inside the chart artifact and one promotion moves both.
- Production is on chart 1.6.9 and staging on 1.7.3. How do you find out what that gap contains?Pull both versions and compare them directly: `helm pull --untar` each version and diff the template trees and default values, or render each with the production values file and diff the manifests. That shows the change production is about to receive, independent of what the git log claims, and it is the check worth running before a promotion that has been sitting for weeks.
saying these in an interview costs you the question
- Deploys each environment from its own chart branch
- Says promotion means re-running the pipeline against prod
- Treats appVersion as the chart's own version
- Points production at the repository's newest version
- Runs dependency update at deploy time in every environment
- Moves chart version and values in the same production window