Flow a Change
With two deployments downstream of one base, a change now has a path to travel: base → dev → prod. In this final step you'll flow a change through the whole graph, verifying it in dev before it reaches production — and see a deployment hold on to a value it has claimed as its own.
A local tweak in dev
Real deployments accumulate local differences. Suppose someone on the dev team renamed the chat title in dev while testing, and wants it to stay that way no matter what the base says later. Make that edit directly on the dev deployment, with --protect:
cub function set --space cubbychat-dev --unit backend --protect set-env-var backend CHAT_TITLE "Cubby Chat (dev)"
--protect records the paths this change writes as protected: they are dev's now, and a merge from the base leaves them alone. Without it the edit still lands, but it claims nothing — a later change to the same field upstream flows in over it, which is what you want for a value the deployment is carrying rather than choosing. Protection is per path, and cub unit set-protection sets it after the fact. Remember this — it matters in a moment.
Change the base
Now the team ships two changes to the base: a company-wide rebrand of the chat title, and a scale-up of the backend.
cub function set --space cubbychat-base --unit backend set-env-var backend CHAT_TITLE "Cubby Chat 2.0"
cub function set --space cubbychat-base --unit backend set-replicas 3
Both deployments are now behind the base. Neither has changed yet — promotion is per-deployment and deliberate.
Promote and verify in dev
Preview which units would flow down, then promote:
cub variant promote cubbychat-dev --dry-run -o mutations
cub variant promote cubbychat-dev
Look at what happened to the backend unit in dev:
cub unit data --space cubbychat-dev backend | grep -E "replicas:|CHAT_TITLE" -A1
The replica count picked up the base's change — it flowed to 3. But CHAT_TITLE is still Cubby Chat (dev), not the base's Cubby Chat 2.0. Dev protected that path, so the promote left it alone while merging in everything else. This is the heart of the model: one change on the base reaches every deployment, without stepping on the differences a deployment has said are its own.
What the promote couldn't apply
The rebrand didn't vanish; it was withheld, and the merge said so. A change a merge can't make is recorded on the unit as a conflict, where it waits until someone decides:
cub unit conflicts --space cubbychat-dev backend
REASON RESOURCE PATH CHANGE
ProtectedPath cubbychat/backend ...env.?name=CHAT_TITLE.value Update Cubby Chat 2.0
ProtectedPath is protection reporting itself. Nothing is broken and nothing is pending in the cluster — dev's data is exactly what dev asked for. What the conflict adds is that you can see what you turned down, rather than the merge silently deciding for you.
Two things you can do with it. See what taking the base's value would mean, without taking it:
cub unit conflicts --space cubbychat-dev backend --apply --dry-run -o mutations
That prints the per-path change it would make — Cubby Chat (dev) → Cubby Chat 2.0 — and writes nothing: no revision, and the conflict is still outstanding afterwards. Drop --dry-run and it would land for real, replacing dev's title with the base's.
Dev wants to keep its title, so accept the state as it is and stop reporting it:
cub unit conflicts --space cubbychat-dev backend --dismiss
Dismissing changes no configuration data and leaves the protection in place — it clears the report, not the decision. The next promote that touches CHAT_TITLE will raise it again, which is what you want: each release gets to ask once.
Release it and check dev is healthy before letting anything near prod:
cub release publish cubbychat-dev
source ~/.confighub/clusters/dev.env
kubectl get pods -n cubbychat
Promote to prod
Preview first — production deserves a look before you promote:
cub variant promote cubbychat-prod --dry-run -o mutations
Prod never claimed the chat title, so nothing there is protected — it takes both changes: CHAT_TITLE becomes Cubby Chat 2.0 and the backend scales to 3. Then:
cub variant promote cubbychat-prod
cub release publish cubbychat-prod
The rebrand is live in prod and in dev — but dev still shows its own title. Same base change, two outcomes, each correct for where it landed.
The rhythm
That's the whole workflow, and it's the same whether you have two deployments or two hundred:
- Change the base once, with functions.
- Promote each deployment when it's ready, previewing the merge — the paths a deployment has protected stay as they are.
- Release each deployment to its target when you're ready for it to be live.
Each step is inspectable and each deployment moves at its own pace — a canary region first, the rest after; dev continuously, prod on approval.
Next
Further reading
- Components
- Variants
- Creating and Managing variants - covers the model in depth, including what a merge writes and what protection withholds from it.
cub cluster downtears down a cluster's live kind cluster; with--delete-configit also deletes the cluster's ConfigHub spaces — the<name>and<name>-argo-appsspaces and every deployment bound to the cluster's target.