Prerequisites: application

The app-of-apps pattern is a single root application whose git path contains nothing but other Application manifests. When argocd syncs the root, it creates those child Applications; each child then syncs the workload it points at. The result is that one Application, applied once by hand, brings up an entire cluster’s worth of components, and from then on you add a component by committing a file, never by running kubectl apply again. It is the pattern that turns “install Argo CD” into “the cluster manages itself”.

The shape

The root Application looks like any other, except its path holds Application YAML instead of Deployments and Services, and it uses directory.recurse so Argo CD picks up every file under that path.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: devata-root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/PragalvaXFREZ/lab.git
    targetRevision: main
    path: kubernetes/clusters/devata   # this folder holds child Applications
    directory:
      recurse: true
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      selfHeal: true
      prune: true

For a small example, if kubernetes/clusters/devata/ contained argocd.yaml, monitoring.yaml, and hello.yaml, syncing devata-root would create those three child Applications. The current directory contains a larger platform set. Each child has its own source pointing at the actual manifests or chart values for that component, destination, and sync policy. The root manages the set of children; each child manages its own workload. This is the same reconciliation loop nested one level: the root reconciles “which Applications should exist”, and each child reconciles “which objects should exist”.

Why it is the bootstrap

Without this pattern, standing up a cluster means applying the Argo CD install, then kubectl apply-ing one Application per component, by hand, in order. That is a runbook a human has to execute correctly. With app-of-apps, the human steps collapse to two: install Argo CD, then apply the one root Application. Everything else is a consequence of git. Adding MetalLB later is not a deployment; it is a commit that drops metallb.yaml into kubernetes/clusters/devata/, which the already-running root notices and creates. Removing a component is deleting its file. The cluster’s entire shape becomes the contents of one directory in git.

This is exactly why the lab repo is laid out the way it is. The kubernetes/bootstrap/ directory is described as “the GitOps controller install and the single root application applied once by hand to start reconciliation”, and kubernetes/clusters/devata/ as “the app of apps and the ApplicationSets that wire up which components from infra/ and which workloads from apps/ run here”. The structure was designed around this pattern before any of it was installed: bootstrap/ is the one manual touch, clusters/devata/ is the set of children, and infra/ and apps/ are the manifests the children point at.

Argo CD managing itself

The most useful child Application is the one that points back at Argo CD’s own install manifests. If kubernetes/bootstrap/ holds the pinned Argo CD install YAML, then a child Application with that as its path and argocd as its destination namespace makes Argo CD reconcile itself. After that, upgrading Argo CD is editing the version in git and letting the running Argo CD apply it. The one subtlety is pruning: a self-managing Application should usually set prune: false, so that a mistaken deletion in git cannot make Argo CD tear down its own controller and leave nothing running to fix it.

ApplicationSet, later

When the children start to look alike (the same Application shape repeated for every folder under infra/), hand-writing each file becomes the chore app-of-apps was meant to remove. An ApplicationSet is a generator that templates Applications from a list, a set of directories, or a set of clusters, producing the children automatically. It is the natural evolution once devata has more than a handful of components, and it is why the repo README names ApplicationSets beside the app of apps. The bootstrap does not need it; one root Application with recurse: true is enough to start, and the move to ApplicationSet is a refactor you make when the repetition is real.

With the Application and this pattern understood, the chapter walkthrough is the hands-on version: install Argo CD, point a root app at clusters/devata/, and watch one manual step turn into a self-reconciling cluster.