Helm, Kustomize and GitOps with Argo CD · Helm Charts That Don't Surprise You
Unpinned Chart Dependencies
Version ranges in Chart.yaml pull in new subchart versions on rebuild; lock and review dependency updates.
Scenario
Example company: an umbrella chart depends on a shared common library chart with the range version: ">=1.0.0". Nothing changed in the app, but a rebuild before a hotfix deploy pulls common 2.0.0, which renames a helper.
What you see in production
The hotfix pipeline fails to render, or worse, renders differently: probes and labels generated by the library changed. The team spends the incident working out why "the same chart" behaves differently.
The naive code
# Chart.yaml
dependencies:
- name: common
version: ">=1.0.0"
repository: https://charts.example.com
# Chart.lock is not committed; `helm dependency update` runs in CI
Reproduce it
Two versions of a local library chart and an app depending on it with a range; helm dependency update resolves the newest, and Chart.lock pins it:
mklib() {
mkdir -p "repo/common-$1/templates"
printf 'apiVersion: v2\nname: common\ntype: library\nversion: %s\n' "$1" > "repo/common-$1/Chart.yaml"
printf '{{- define "common.greeting" -}}common %s{{- end -}}\n' "$1" > "repo/common-$1/templates/_helpers.tpl"
helm package -d repo "repo/common-$1" > /dev/null
}
mklib 1.4.0
helm repo index repo > /dev/null
mkdir -p app/templates
printf 'apiVersion: v2\nname: app\nversion: 0.1.0\ndependencies:\n - name: common\n version: ">=1.0.0"\n repository: file://../repo/common-1.4.0\n' > app/Chart.yaml
printf 'apiVersion: v1\nkind: ConfigMap\nmetadata: {name: info}\ndata: {built-with: {{ include "common.greeting" . | quote }}}\n' > app/templates/info.yaml
helm dependency update app > /dev/null
echo "locked: $(grep -A2 'name: common' app/Chart.lock | grep version | xargs)"
echo "renders: $(helm template app ./app | grep built-with | xargs)"
mklib 2.0.0
sed -i 's#common-1.4.0#common-2.0.0#' app/Chart.yaml # a newer version becomes available in the repository
helm dependency update app > /dev/null
echo "after an unreviewed update: $(grep -A2 'name: common' app/Chart.lock | grep version | xargs), renders: $(helm template app ./app | grep built-with | xargs)"
Output
locked: version: 1.4.0
renders: data: {built-with: common 1.4.0}
after an unreviewed update: version: 2.0.0, renders: data: {built-with: common 2.0.0}Run this on your computer — see “Get Started” for setup.
Root cause
Version ranges resolve to the newest matching release at the moment helm dependency update runs. If the lock file isn't committed, or CI re-resolves dependencies on every build, two builds of the same commit can use different subchart versions, including new major versions with breaking changes.
The fix
- Use exact versions or tight ranges (
~1.4.0) inChart.yaml. - Commit
Chart.lockand runhelm dependency build(which uses the lock) in CI, notupdate. - Update dependencies deliberately in a pull request (Renovate or Dependabot can open them) and review the rendered diff.
- Mirror third-party charts into your own registry (OCI) so upstream deletions or changes don't affect builds.
Trade-offs
- Pinning means you must actively take security fixes; automate update PRs.
- Mirroring adds a registry to operate.
Detect it in production
- CI check that
Chart.lockis committed and matchesChart.yaml. - Rendered manifest diffs on dependency update PRs.
Key points
- Ranges float;
helm dependency updatepicks the newest match. - Pin versions, commit Chart.lock and build from the lock in CI.
- Update dependencies in reviewed PRs with rendered diffs.
Further reading: Chart dependencies · Use OCI-based registries
Practice questions
What is the difference between helm dependency update and helm dependency build?
Answer
update resolves Chart.yaml's version ranges to the newest matching versions and rewrites Chart.lock; build installs exactly the versions recorded in Chart.lock.
Why commit Chart.lock?
Answer
So every build of a commit uses the same subchart versions; without it, builds re-resolve ranges and can pick up new, possibly breaking, versions.
How do you keep pinned chart dependencies up to date safely?
Answer
Let a bot (Renovate, Dependabot) open update pull requests, and review the rendered manifest diff and changelog before merging.
Open in the app to mark lessons complete, take notes and track your progress.