Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 8 min read

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 AppProject to 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 edit on 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.

Free download · 368-page PDF

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.