1. What changes, and what does not
Argo CD stays the delivery controller. Preview has no account or cluster mutation; import makes a parallel ConfigHub copy while Git still delivers; handover then moves sources to reviewed releases. Handover has checks and recovery steps, not zero risk.
For an app-of-apps estate, ConfigHub keeps the root management structure and its parent-to-child relationships visible. Argo CD still renders charts and reconciles the descendants. The plan inventories descendant Applications and their rendered objects so you can inspect what the root governs; controller-generated or live objects remain evidence unless you deliberately choose them as desired configuration.
Start with a small disposable estate. Roots, generators, and pruning can make production migration an ordered fleet operation.
Who does what, before and after
| Before the handover | After the handover | |
|---|---|---|
| Source of truth | Git | ConfigHub |
| Argo CD | syncs each Application from Git | still syncs each Application, from the release ConfigHub published |
| A change | is a commit to the Git path | is an edit in ConfigHub, released after the approvals its stage asks for |
| A commit to the old Git path | reaches the cluster | no longer reaches the cluster |
The handover is the step that moves the source of truth. Until handover.sh runs, ConfigHub holds a copy that nothing reads. The Argo CD install, AppProject permissions, sync windows and cluster credentials stay where they are.
Some teams on Flux? Bring your Flux fleet into ConfigHub follows the same steps.
2. Preview the setup
Install the cub CLI and this released plugin:
cub plugin install confighub/examples@cub-argo-v0.4.1 --name argo
What you need: a local checkout of the Git repository this Argo CD installation reads, the cub CLI, and this plugin. Start in that repository's root; replace the directory placeholder below with its path. Planning reads the repository and needs no ConfigHub account or cluster.
cd <your-argo-gitops-repository>
cub argo plan .
Read the result. The summary gives the estate or fleet size and the component and variant counts. The following lines map shared configuration to bases and cluster differences to variants. Check the stage order and warnings before continuing. This plan does not validate the destination.
Illustrative plan output
This is actual output from the public beginner fixture, not from your repository.
Argo CD estate: 1 cluster, Argo CD's own (in-cluster), 2 components, 2 variants
Read 9 objects
Control tree (stays as it is: this is the management record)
Application apptique-apps wave 0 root, applied by hand
Application apptique-dev wave 0 deploys workloads; planned below
Application apptique-prod wave 0 deploys workloads; planned below
One stage, fleet (pass --stage-label and --stages to roll out in waves)
apptique-dev (Application, project default, wave 0)
base argo-apptique-dev-base reaches no cluster: its destination is empty
each variant differs from the base in spec.destination
stage fleet
in-cluster variant argo-apptique-dev-in-cluster -> Target argo-targets/in-cluster
Application apptique-dev, namespace apptique-dev
gitops/argo/beginner-app-of-apps/manifests/apptique/dev
note a plain directory of manifests, read as Argo CD reads it: every .yaml, .yml and .json file at the top level
apptique-prod (Application, project default, wave 0)
base argo-apptique-prod-base reaches no cluster: its destination is empty
each variant differs from the base in spec.destination
stage fleet
in-cluster variant argo-apptique-prod-in-cluster -> Target argo-targets/in-cluster
Application apptique-prod, namespace apptique-prod
gitops/argo/beginner-app-of-apps/manifests/apptique/prod
note a plain directory of manifests, read as Argo CD reads it: every .yaml, .yml and .json file at the top level
Handover, when this estate is live (apply will write it as handover.sh; plan runs nothing)
1. check Argo CD is v3.1 or newer, which is where an oci:// source is read natively; an older one cannot do this at all
2. repoint Application apptique-apps at argo-apptique-apps-children, which would hold Application apptique-dev, Application apptique-prod. Publish that Space first: a parent left syncing an empty source prunes its children. Nothing above it syncs it, so patch its spec.source in the cluster.
3. never delete apptique-apps, apptique-dev, apptique-prod: resources-finalizer.argocd.argoproj.io deletes everything it deployed. Every step above is a patch for exactly this reason
Next
cub argo apply . --out ./argo-onboarding
That writes apply.sh, handover.sh and cleanup.sh. It runs nothing.
Read the full Argo CD planning reference for layouts and limits.
Try it on the fixture first
These commands print the output above. They need no repository of your own.
git clone https://github.com/confighub/examples.git
cd examples
git checkout 7f1b8f2fc849bb6488bc2c58f1469172018fc9dd
cd gitops/argo/beginner-app-of-apps
cub argo plan .
Run the plan inside a Git checkout. In a copy with no .git directory the plan reports source paths as missing, and --repo-root <checkout> tells it where the repository starts.
What one Application becomes
cub argo apply . --out onboard writes files only. For each Application it writes the copy Argo CD has today under onboard/control/, and the copy it would have after the handover under onboard/repointed/. Each copy sits in a folder named for the Space that holds it, which is argo-apptique-apps-children on the fixture. The two copies of apptique-dev differ in three lines.
kind: Application
metadata:
name: apptique-dev
spec:
source:
- repoURL: https://github.com/confighub/examples.git
- targetRevision: main
- path: gitops/argo/beginner-app-of-apps/manifests/apptique/dev
+ repoURL: oci://<gateway>/space/argo-apptique-dev-in-cluster
+ path: .
+ targetRevision: latest
The Application keeps its name, project, destination and sync policy. It reads the Space the plan names as its variant, argo-apptique-dev-in-cluster.
An Application with helm or kustomize settings under source loses those settings in the repointed copy, because what Argo CD reads afterwards is already rendered. The fixture has no such Application, so this page shows none.
How to go back
| After this step | Go back by |
|---|---|
cub argo plan . | Nothing to undo. |
cub argo apply . --out onboard | Delete onboard/. |
bash onboard/apply.sh | Run bash onboard/cleanup.sh. It removes what the script made in ConfigHub. Argo CD still reads Git. |
bash onboard/handover.sh | Put each Application's source back to Git first, leaves before parents. The script records each source before it changes it and prints the commands. Do not delete the ConfigHub Spaces first. Never delete the Applications, because their finalizer deletes what they deployed. |
You can stop here. The plan is read-only. To generate local scripts, connect delivery, or make a reviewed change, open the advanced steps below.
After the preview: save, connect delivery and make a change
3. Save the reviewed setup in ConfigHub
When the plan is clear, generate the setup directory. This writes files and scripts only.
cub argo apply . --out onboard
Read onboard/; delete it to discard this step. Argo CD keeps reading Git while you review the generated setup.
Check what a source will store
apply.sh stores what each Application's source path renders to. Before you run it, you can check a source with cub config check from the Workshop plugin. It reads files only. On the fixture, apptique-dev reads manifests/apptique/dev, a folder of plain manifests, so the step joins its files. A path with a kustomization.yaml builds with kustomize build instead:
cub plugin install confighub/cub-workshop@v0.6.61
for f in manifests/apptique/dev/*.yaml; do cat "$f"; echo ---; done > apptique-dev.yaml
cub config check apptique-dev.yaml
On the fixture it reports 3 objects (a Deployment, a Service and a ServiceAccount), no lifecycle work, and a passing schema check. The schema step names any field the API server would reject, so you can fix it in Git before you save.
Only the next command saves ConfigHub data. It needs cub auth login, kustomize, and any requested gateway. It creates bases, variants, stages, and releases, not cluster changes.
bash onboard/apply.sh
Keep this import separate from production handover; practice first with a disposable estate.
4. Connect deployment
This first cluster-changing step checks the reviewed release against the controller record before moving a source. Replace every angle-bracket value below.
The handover repoints the root and related app-of-apps layers in their required order. Generated Applications are moved later, stage by stage; this is an estate operation, not a one-app shortcut. Do not assume production can move one application at a time.
Review ownership first. Pruning can delete an object absent from the reviewed release. Confirm the printed way back, ownership, backups, and objects. Exporting cluster registration data for planning must remove credentials. Keep workload Secrets and OCI pull credentials out of the repository and out of ConfigHub review data.
Contexts, gateway, and fleet handover parameters
A gateway is the OCI address controllers use to pull ConfigHub releases: ConfigHub Cloud uses oci.hub.confighub.com, without a scheme. A context is the named kubeconfig context that reaches the stated cluster.
Argo CD needs its own context and one destination context per remote cluster. This is an example for a remote cluster named prod-1; use the variables the generated script names for your own estate.
# Example only: remote cluster prod-1
ARGOCD_CONTEXT=<argo-context> DEST_CONTEXT_prod_1=<prod-1-context> CONFIGHUB_OCI=oci.hub.confighub.com bash onboard/handover.sh
Advanced handover and recovery reference. Do not delete ConfigHub spaces as rollback: restore controller sources first, then use the script’s printed order.
5. Change, approve, and release
After handover, a reviewed ConfigHub change moves through stages. Independent approval is not default; configure it and separate credentials when needed.
A small change, approval, and first release
The released Argo example first writes a local apptique.yaml input, edits it, then stores, promotes, approves, and publishes it:
cub unit data --space argo-apptique-base apptique > apptique.yaml
# edit apptique.yaml before the next command
cub unit update --space argo-apptique-base apptique apptique.yaml --change-desc "Raise the frontend to 4 replicas"
cub changeorder create --space argo-apptique-base more-replicas --change-workflow argo-apptique-base/rollout --description "Raise replicas"
cub variant promote --change-order argo-apptique-base/more-replicas --target-stage canary
cub variant approve --change-order argo-apptique-base/more-replicas --stage canary
cub release publish argo-apptique-dev-1 --revision ChangeOrder:argo-apptique-base/more-replicas
Read the released worked example before using its example Space names.
Live status is also a write. This sends the observed revision and health to ConfigHub. Argo CD also gets a hard refresh.
cub argo status . --kube-context <argo-context> --watch --hard-refresh
Read the Argo CD status details. A release, controller sync, and workload health are separate results; record the exact revision and target for each.