Stages and sources

A StageSet is an ordered list of stages. Each stage resolves a
Flux source — a GitRepository, OCIRepository, Bucket,
or an ExternalArtifact (the default) — applies its manifests, waits for them to
become healthy, and only then lets the next stage start.
One stage
The minimum is one stage pointing at one artifact in the same namespace:
apiVersion: stages.metio.wtf/v1
kind: StageSet
metadata:
name: my-app
namespace: default
spec:
stages:
- name: app
sourceRef:
name: my-app # an ExternalArtifact
sourceRef.kind defaults to ExternalArtifact, so the common case is a single
line. The controller fetches the artifact, applies every manifest in it, and marks
the stage Ready once the applied objects report healthy.
Source kinds
A sourceRef resolves to a Flux artifact three ways. Point it at whichever you
already have:
# 1. an ExternalArtifact (the default — kind omitted)
sourceRef:
name: my-app
# 2. a classic Flux source, consumed directly
sourceRef:
kind: GitRepository # or OCIRepository, or Bucket
name: my-app-manifests
# 3. a producer that publishes an ExternalArtifact (resolved via its back-pointer)
sourceRef:
apiVersion: jaas.metio.wtf/v1
kind: JsonnetSnippet
name: my-app
GitRepository, OCIRepository, and Bucket carry the same status.artifact
contract as ExternalArtifact, so the controller reads them directly — no producer
in between. A stage can apply manifests straight from a Git repo or an OCI artifact,
like Flux’s own kustomize-controller. For the producer case (for example
rendering Jsonnet with JaaS), see
producer-aware sources.
Ordered stages
Add more stages and they run top to bottom — each one waits for the previous to be
Ready:
spec:
stages:
- name: crds # 1 ── install the CRDs first
sourceRef:
name: platform-crds
- name: operator # 2 ── then the operator that needs them
sourceRef:
name: platform-operator
- name: workloads # 3 ── then the workloads it manages
sourceRef:
name: team-workloads
This is the core of a StageSet: operator is never applied until crds is
healthy, so the operator never crash-loops waiting for a CRD that isn’t there yet.
Shaping a stage’s manifests
A stage can build from a sub-path of the artifact, customize with patches, and substitute variables — the kustomize-style surface:
spec:
stages:
- name: app
sourceRef:
name: my-app
path: ./overlays/production # build a sub-path of the artifact
prune: true # GC objects that leave this stage (default)
patches:
- patch: |
- op: replace
path: /spec/replicas
value: 6
target:
kind: Deployment
name: web
postBuild:
substitute:
cluster_name: prod-eu
substituteFrom:
- kind: ConfigMap
name: cluster-vars
- kind: Secret
name: cluster-secrets
optional: true
pathbuilds from a directory inside the artifact (default./).prune(defaulttrue) garbage-collects objects that fall out of the stage between reconciles, tracked precisely via the stage’sStageInventory.patchesare strategic-merge or JSON6902 patches applied after the build.postBuildsubstitutes${var}references from inline values, ConfigMaps, and Secrets at delivery time — see parameterizing a rollout for the full render-time-vs-delivery-time treatment.
From here, layer on actions to gate the stage, or ready checks to define what “healthy” means.