StageSet vs Helm see history edit this page

Talks about: , , and

Helm is two things: a templating engine (charts) and an imperative release tool (helm upgrade). StageSet is neither — it’s a declarative delivery controller. They overlap on one axis: sequencing work around a release. Helm expresses that with hooks and hook weights inside a single chart’s install or upgrade; StageSet expresses it as ordered, gated stages with typed actions, under continuous reconciliation.

Helm stays the better tool for what it was built for: packaging an application, parameterizing it with values, and distributing that chart to others. StageSet starts once you have manifests and need to deliver them carefully — so the two compose rather than compete.

What Helm gives you

Where StageSet differs

Run something once — and know it ran

This is the sharpest difference, and it’s a capability Helm has no vocabulary for. A Helm hook fires on a fixed schedule tied to the release operation:

StageSet makes that a first-class, declared property of each action — its scope :

Underneath the vocabulary is the real difference: a delivery controller remembers what it has done and can be asked to prove it, where a package manager re-derives its plan from the chart on each run. Adopting a system whose bootstrap already ran, exporting that record for disaster recovery, and deliberately forgetting a completion so it re-runs are all first-class operations (stagesetctl baseline , reset-ledger ).

Roll back — safely, or refuse

helm rollback re-applies a previous release’s manifests. For a stateless app that is exactly right, and it is simpler than anything here. For a stateful app it is a trap: rolling 2.0 back to 1.0 re-applies the old manifests but does nothing about the schema migration the 1.0 → 2.0 upgrade ran. The database stays at the 2.0 schema while the code is 1.0 — and Helm reports the rollback succeeded.

StageSet models the downgrade instead of treating manifests as the whole story. A migration can declare down actions — the inverse of its up actions — and a downgrade (opt-in via spec.version.allowDowngrade) unwinds every crossed boundary in reverse, running those down actions, before lowering the version. A boundary that declares no down actions is irreversible: the downgrade is refused, naming the migration, rather than lowering the version while its schema change stands.

The honest limit: StageSet reverses only what a down action declares — a genuinely destructive change like a dropped column still needs a down that restores it from backup. But a refusal with a reason is strictly safer than a rollback that silently leaves the schema ahead of the code, and stagesetctl plan shows which boundaries reverse and which refuse before you merge the version change.

Roll a version across a fleet

Helm has no fleet concept. Rolling a new chart version across a hundred tenants means running helm upgrade a hundred times — in a loop, a CI matrix, or an Argo ApplicationSet — with no built-in notion of canary, soak, health gate, or “stop everything if the first tenant regresses.” You assemble that from external glue.

A FleetRollout makes it declarative: one target version, ordered waves, a soak and a metric gate between waves, and a fleet-wide halt when a wave regresses — one canary regression stops the other ninety-nine before they start. It approves the version each tenant’s own source already offers, so it paces GitOps rather than replacing it. Helm would have to grow a fleet, a version model, and a health-gated wave machine to match it — which is to say, become a delivery controller.

Using them together

Render a chart to manifests (e.g. via a producer that publishes an ExternalArtifact) and deliver it with StageSet. The controller understands helm.sh/hook resources: applyHelmHookResources (default true) applies them as ordinary objects, so a Helm-style chart’s hook resources still get created — under StageSet’s ordering, gating, and action lifecycle rather than Helm’s.