What is the difference between helm dependency update and helm dependency build?
answer
- One command decides, the other obeys
- Ranges feed only one of the two
- Which file is the source of truth?
- Missing lockfile changes the second command's behaviour
- Resolving nothing still fetches something
basics
~20 shelm dependency update re-resolves the ranges in Chart.yaml, rewrites Chart.lock and refills charts/. helm dependency build ignores the ranges and fetches exactly what Chart.lock already pins, so pipelines run build and authors run update deliberately.
solid answer
~50 s`helm dependency update` is the *authoring* command. It reads the `dependencies:` ranges in `Chart.yaml`, resolves each one against its repository, downloads the chosen archives into `charts/`, and writes a fresh `Chart.lock`. Running it can change which subchart versions you get, which is why it belongs in a commit a human reviews. `helm dependency build` is the *consuming* command. It reads `Chart.lock`, never re-evaluates the ranges, and populates `charts/` with exactly the pinned versions. If the lock's digest no longer matches the dependency declarations in `Chart.yaml`, it refuses and tells you the two are out of sync instead of installing stale pins. The trap: with no `Chart.lock` present, `build` falls back to the behaviour of `update`. A pipeline that runs `build` on a chart whose lock was never committed silently resolves ranges on every run while looking pinned.
code
bash · 8 lines# Author, deliberately: re-resolve ranges and rewrite the pins
helm dependency update ./invoice-worker
git add invoice-worker/Chart.lock
git commit -m "chore(deps): bump invoice-operator to 2.15.1"
# CI: materialise exactly what the lock pins, resolving nothing
helm repo add internal https://charts.example.com
helm dependency build ./invoice-workergo deeper
Remember which file each command trusts: update reads the ranges in Chart.yaml and rewrites Chart.lock, build reads Chart.lock and fetches exactly those versions into charts/.
Explain the mechanics - resolution versus materialisation, the digest guard that makes build refuse an out-of-sync lock, and the fallback where build with no lock behaves like update.
Show the operational consequence: pipelines run build, humans run update in a reviewable commit, and you assert in CI that the lock exists and is unchanged after the build.
Own the rule for the org - resolution happens in pull requests, never in deploy jobs - and be ready to say how you keep pins from rotting once no build is allowed to bump them.
## Two commands, two different jobs Both commands end with a populated `charts/` directory, which is why they get confused. They differ in what they treat as the source of truth. **`helm dependency update`** takes `Chart.yaml` as truth. It refreshes the local repository cache, evaluates each dependency's `version` range against the versions the repository offers, picks the highest match, downloads those archives into `charts/`, and writes `Chart.lock` recording what it picked. It is a *resolution*. Two runs a month apart on unchanged source can legitimately produce different results, because upstream published something new in the meantime. **`helm dependency build`** takes `Chart.lock` as truth. It reads the pinned `name`/`version`/`repository` triples and fetches precisely those, ignoring the ranges entirely. It is a *materialisation*, not a resolution. Two runs a year apart give the same versions. There is one guard between them. The lock's `digest` is a hash over the dependency declarations. If someone edits `Chart.yaml` - adds a subchart, widens a range - and does not regenerate the lock, `build` refuses to proceed and reports `Chart.lock` as out of sync with `Chart.yaml`, rather than installing pins that no longer correspond to what the chart asks for. ## The fallback that bites When `helm dependency build` finds no `Chart.lock` at all, it does not fail. It mirrors `helm dependency update`: it resolves the ranges and writes a lock. This is documented and it is the single most common way a team believes it has reproducible chart builds and does not. `Chart.lock` is in the repository's `.gitignore`, or was never committed for a chart added last quarter, and every pipeline run has been quietly resolving `^2.14.0` to whatever is newest. Nothing errors; the deploy just changes one Tuesday when upstream cuts `2.16.0`. The defence is a pipeline assertion, not a Helm feature: fail the job if `Chart.lock` is absent, and fail it if `helm dependency build` leaves the working tree dirty - a modified lock after a build means the build resolved something. ## What `build` still does over the network "Resolves nothing" is not "contacts nothing". `build` still has to *fetch* each pinned archive unless it is already sitting in `charts/`. For a classic chart repository that means the repository must be reachable and known to the client, so a fresh CI container typically needs `helm repo add` first, or `build` fails because it has no cached index for that repository; for an `oci://` dependency it needs registry access and, if the registry is private, a prior `helm registry login`. Both commands accept a flag to skip refreshing the local repository cache when that refresh is the slow part. So the axis of reproducibility (`update` vs `build`) is separate from the axis of network dependence (fetch vs vendored `charts/`). Only committing the resolved archives in `charts/`, or packaging the chart once into a `.tgz` that already carries them, removes the fetch. ## Where each belongs - **Author's laptop, deliberately**: `helm dependency update`, followed by a commit that contains the changed `Chart.lock` - and, if you vendor, the changed `charts/`. The diff of the lock is the review artefact: `2.14.3` to `2.15.1` is a question a reviewer can ask about. - **CI, release pipelines, anything automated**: `helm dependency build`. It cannot pick up a version nobody approved. - **Never**: `helm dependency update` inside a deploy job. That turns every deploy into an unreviewed dependency bump, and it means a rollback of your chart does not roll back the subchart, because the pins are recomputed on the way. A related flag worth knowing is `helm package --dependency-update` (`-u`), which runs an update before packaging. It is convenient for a chart you are cutting from a freshly reviewed lock and dangerous as a habit for the same reason: it resolves at package time. ## Diagnosing the common failures - *"found in Chart.yaml, but missing in charts/ directory"* on `helm install` or `helm template` against a source directory: a declared subchart was never materialised. Run `build` (or `update` if there is no lock). - *Out of sync* from `build`: `Chart.yaml` changed after the lock was written; run `update` and commit both files together. - *Build works locally, fails in CI with no cached index*: the repository is in your local Helm config and not in the runner's. Add the repository in the job, or vendor `charts/` so nothing is fetched. Saying "CI runs build, humans run update, and build with no lock quietly behaves like update" is the whole answer at senior depth.
- A pipeline runs helm dependency build on a chart with no Chart.lock. What actually happens?It succeeds, and it resolves. With no lock present, `build` mirrors `helm dependency update`: it evaluates the ranges in `Chart.yaml`, fetches the newest matching versions and writes a lock into the job's workspace, which is then thrown away with the runner. The job looks pinned and is not, so the deploy can change with no commit behind it. Guard it by failing the job when `Chart.lock` is missing.
- Why should helm dependency update never run inside a deploy job?Because it makes every deploy an unreviewed dependency bump. The versions installed then depend on the moment the job ran rather than on anything in the commit, two environments deployed from the same SHA can get different subcharts, and re-deploying an older revision of your chart does not restore the subchart versions it originally shipped with. Resolution belongs in a human-reviewed commit; deploys should only materialise a lock.
- Does helm dependency build work with no network access?Only if the pinned archives are already in `charts/`. `build` resolves nothing, but it still has to fetch each pinned chart - from a classic repository, which must be reachable and added to that client's cache, or from an `oci://` registry, which may need a prior login. A truly offline build needs the subchart tarballs vendored in `charts/`, or an already-packaged chart `.tgz`, which carries its dependencies inside it.
saying these in an interview costs you the question
- Says the two commands are interchangeable aliases
- Runs helm dependency update in the deploy pipeline
- Thinks build fails when Chart.lock is missing
- Believes build never touches the network
- Claims update leaves Chart.lock untouched
- Says build re-checks ranges to allow newer patch releases