Tuesday, October 27, 2026

GitOps with Argo CD: The App-of-Apps Pattern

Once you have more than a handful of applications in Argo CD, clicking “New App” in the UI stops scaling. Nobody remembers which settings were used, and rebuilding a cluster means recreating every Application by hand. The app-of-apps pattern puts the Argo CD Applications themselves in Git, managed by one root Application.

Repository layout

gitops/
├── root-app.yaml            # applied once, by hand
├── apps/                    # one Application per service
│   ├── account-search.yaml
│   ├── payments-api.yaml
│   └── ingress-nginx.yaml
└── charts/ or manifests/    # the actual workloads

The root Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://git.example.com/platform/gitops.git
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Apply it once with kubectl apply -n argocd -f root-app.yaml. From then on, adding a service is a pull request that drops a new file into apps/.

A child Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: account-search
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: "10"
spec:
  project: apps
  source:
    repoURL: https://git.example.com/platform/gitops.git
    targetRevision: main
    path: charts/account-search
    helm:
      valueFiles: [values-prod.yaml]
  destination:
    server: https://kubernetes.default.svc
    namespace: account-search
  syncPolicy:
    automated: { prune: true, selfHeal: true }
    syncOptions: [CreateNamespace=true]

Tips from running it

  • Use sync waves for ordering. Give platform pieces (ingress controller, cert-manager, operators) a lower sync-wave than business apps so CRDs exist before anything uses them.
  • Use AppProjects as guard rails. Restrict which repos and namespaces the apps project can deploy to, so a typo cannot deploy into kube-system.
  • Be careful with prune on the root. Deleting a file in apps/ deletes the Application and, with the default finalizer, its workloads. That is usually what you want, but review those PRs carefully.
  • Consider ApplicationSets once the child files all look the same. A Git directory generator can create one Application per folder automatically.
  • Keep Argo CD patched. The repo-server holds your Git and Helm credentials; treat its upgrades like any other internet-facing component.

The payoff: a new cluster becomes “install Argo CD, apply one file, wait”, and the state of every environment is reviewable in Git.

No comments: