Skip to content

Migrating to Pinned Installs

llms.txt lists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/chant/lexicons/helm/migration/.
Move the unpinned release I name onto a pinned render: render it with `HelmRender` under the release name it already runs under, persist the render, and change the `helmInstall` call to `{ name, contentDigest, renderStoreRoot }` with `chart`, `chartVersion`, `values` and `set` removed.
Run `npx chant build` and `npx chant lint` until both are clean, and open a pull request with the change and the content digest.
Print the deploy command and the `helm history <release>` check for a person to run; do not run them.
Never deploy to a real cloud account or shared environment (with `chant run`, `chant components` or a platform's own CLI), approve (a pull request review, `chant approve` or `chant run approve`), override a policy denial (`chant approve policy-override`), or merge; never cut or publish a release (`chant release cut --yes` or `chant release publish`), push or delete a `ci/*` tag (`chant ci tick` pushes them), or change the value of an existing flag; never touch `.chant/allowed_signers` or `chant/lifecycle`.

A release deployed by the unpinned path can move onto a pinned render with an ordinary helm upgrade --install, the same command the pinned path already runs. The release upgrades in place, with no uninstall and no recreated resources.

Deploy the pinned render under the same release name the unpinned release already runs under.

// Was:
await helmInstall({ name: "web", chart: "./chart", chartVersion: "1.2.0" });
// Migrates to:
await helmInstall({ name: "web", contentDigest, renderStoreRoot });

helm upgrade --install targets a release by name. When a release named web already exists, it upgrades that release in place: same object, next revision, helm history web continuous across the switch. The release already exists, so this is just its next revision.

The wrapper chart the pinned path materializes keeps the source chart’s name and version (RoutedRender.chart / chartVersion; see Helm Concepts), so helm history shows the same chart throughout.

Installing the pinned render under a different name looks like a safer, more explicit migration: a fresh helm install web-v2 <wrapper> next to the existing web. It fails instead, with an ownership-metadata error: the resources web already owns (their meta.helm.sh/release-name / meta.helm.sh/release-namespace annotations) belong to web, and helm refuses to let a second release claim them.

helmInstall refuses this earlier and offline, before any helm command runs. A contentDigest is rendered for exactly one release name (RenderManifest.releaseName; .Release.Name is baked into the recorded bytes, labels and resource names included), and a deploy that targets a different name throws PinnedInstallInputError naming both:

pinned install refused: render sha256:... was rendered for release "web" but the deploy
targets release "other". `.Release.Name` is baked into the recorded bytes (labels, resource
names), so installing them under another name deploys bytes that claim a different release.
Deploy under "web", or render for "other". Nothing was deployed.

Render the migration target under the release’s own name (HelmRender with releaseName: "web"), then deploy that digest. Never deploy a digest rendered for a different name.

  • CRDs stay untouched. Helm applies crds/ only on helm install, never on helm upgrade (see Best Practices). Migrating an existing release is an upgrade of that release, so its already-installed CRDs (including ones a subchart shipped) are left exactly as they are. The wrapper still carries them in crds/, uninstall-safe, for the release’s next fresh install.
  • Hooks still fire. Hook documents route separately from the main manifest but ship through the same wrapper templates, so helm registers and runs them on the upgrade exactly as it would for any other revision.
  • Unrelated resources are unchanged. A ConfigMap (or any other resource) whose rendered bytes did not change between the unpinned and pinned renders reaches the cluster byte-identical; the pinned path installs the render store’s recorded bytes verbatim, never re-templating them.
  • The revision advances by one, same as any other helm upgrade. Nothing is deleted and recreated to make the switch.
  1. Render the chart for pinning with HelmRender, passing the exact releaseName the live release already runs under.
  2. Persist the render and note its contentDigest.
  3. Deploy with helmInstall({ name: <same release name>, contentDigest, ... }). Drop chart, chartVersion, values and set, which the pinned path refuses alongside contentDigest.
  4. Confirm with helm history <release>: the revision after the deploy is one more than before, same chart name, no gap.

chant