Scaling Argo CD With the App-of-Apps Pattern
Managing a hundred Argo CD applications by hand doesn't scale. The app-of-apps pattern lets one root application bootstrap your entire fleet.
- #kubernetes
- #argocd
- #gitops
- #app-of-apps
- #ci-cd
- #helm
Argo CD is wonderful right up until you have fifty applications and someone has to kubectl apply each Application manifest by hand whenever a new cluster spins up. That’s not GitOps — that’s GitOps with a manual bootstrap step bolted on. The app-of-apps pattern closes that gap: one root Argo CD application manages a directory of other Argo CD applications, so the entire fleet declares itself from Git. I’ve used it to bootstrap clusters from zero to fully reconciled with a single apply, and it’s the pattern I reach for the moment a setup outgrows a handful of apps.
The core idea
An Argo CD Application can point at a Git path that itself contains more Application manifests. Argo CD reconciles the root app, which creates the child apps, which each reconcile their own workloads. You bootstrap one thing; everything else cascades.
root-app ──> apps/
├── cert-manager.yaml
├── ingress-nginx.yaml
├── monitoring.yaml
└── team-payments.yaml
Apply the root once and Argo CD pulls the rest into existence.
The root application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme/cluster-config.git
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
The path: apps directory holds a set of child Application manifests. With automated, prune, and selfHeal on, any app you add to that directory and merge to main appears in the cluster automatically, and any app you delete from Git gets pruned.
A child application
Each child is an ordinary Argo CD app pointing at the actual workload:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: monitoring
namespace: argocd
spec:
project: platform
source:
repoURL: https://github.com/acme/cluster-config.git
targetRevision: main
path: charts/kube-prometheus-stack
destination:
server: https://kubernetes.default.svc
namespace: monitoring
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
To onboard a new component, you add one file like this to apps/ and merge. That’s the entire workflow.
Ordering with sync waves
Some things must exist before others — cert-manager’s CRDs before any Certificate, the namespace before the workload. Argo CD sync waves give you ordering via an annotation:
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"
Lower waves sync first. I put CRD-providers and namespaces at wave -2, platform services at -1, and application workloads at 0. This is what stops the classic bootstrap race where a workload tries to create a custom resource whose CRD hasn’t been installed yet.
ApplicationSets: app-of-apps on autopilot
Once you’re managing the same set of apps across multiple clusters or generating apps from a list, hand-writing each child becomes the new toil. ApplicationSet templates them from a generator:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: tenants
namespace: argocd
spec:
generators:
- list:
elements:
- tenant: payments
- tenant: search
- tenant: billing
template:
metadata:
name: '{{tenant}}'
spec:
project: default
source:
repoURL: https://github.com/acme/cluster-config.git
targetRevision: main
path: 'tenants/{{tenant}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{tenant}}'
syncPolicy:
automated: { prune: true, selfHeal: true }
Add {tenant: marketing} to the list and a fully configured tenant app appears. The cluster generator does the same thing across registered clusters — one definition, every cluster gets the app. For large fleets this is the real endgame; app-of-apps is the stepping stone.
Operational discipline that keeps it safe
A pattern this powerful needs guardrails:
- prune is a loaded gun. With
prune: true, deleting an app from Git deletes it from the cluster. That’s the point — but a bad merge can prune production. Protect the config repo with required reviews and branch protection. - Scope Argo CD Projects. Don’t let every app deploy anywhere. Use
AppProjectto restrict which repos, namespaces, and cluster resources an app can touch. The root app especially should be tightly scoped. - Watch for sync storms. A change to a shared chart referenced by many children can trigger dozens of simultaneous syncs. Stagger with sync waves or sync windows if it overwhelms the cluster.
- selfHeal fights manual changes. That’s intended — it reverts drift — but it means
kubectl editon a managed resource snaps back. Make changes in Git, not the cluster.
Verifying the cascade
kubectl get applications -n argocd
argocd app get root
argocd app sync root
The root app’s tree view in the Argo CD UI shows every child and its health, which is the fastest way to spot a stuck bootstrap — usually a sync-wave ordering issue or a child pointing at a path that doesn’t exist yet.
Because a single merge to the config repo can reshape an entire cluster, this is exactly the kind of change worth a careful review before it lands. Our AI code review is useful for catching an over-broad AppProject or a prune-enabled app whose destination namespace looks wrong.
App-of-apps turns cluster bootstrap from a runbook into a single apply, and ApplicationSets take it the rest of the way to fleet scale. For more GitOps and operations guides, see the Kubernetes & Helm category.
GitOps automation can prune real workloads. Verify project scoping and prune settings against your own clusters before enabling automated sync in production.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.