App-of-Apps vs ApplicationSet - When to Use Which#
The Core Difference#
Both patterns answer the same question: how do I manage many ArgoCD Applications without manually applying each one? But they solve it at different levels.
App-of-Apps - a manually maintained parent Application that watches a directory of hand-written child Application manifests. You own every YAML.
ApplicationSet - a controller that generates Application manifests from a template + a data source (a git directory, a list of files, a cluster list, etc.). You define one template; ArgoCD stamps out N Applications.
Side-by-Side Comparison#
| App-of-Apps | ApplicationSet | |
|---|---|---|
| Application manifests | Hand-written per service | Auto-generated from template |
| Adding a new service | Commit a new Application YAML | Create a new folder or values file |
| Per-service customisation | Easy - just edit the YAML | Via generator parameters or values files |
| Platform controls sync policy | No - each service owns its YAML | Yes - template is fixed by platform team |
| Services are homogeneous (same chart) | Works but verbose | Better fit |
| Services are heterogeneous (mixed charts) | Natural fit | Awkward |
| Multi-cluster deployments | Manual - one YAML per cluster | Built-in via Cluster generator |
| Bootstrap cost | kubectl apply once per stage | kubectl apply once per ApplicationSet |
The Access Control Question#
A common constraint in real teams: dev teams cannot kubectl apply into the
argocd namespace. This rules out both manually applying Application YAMLs
and posting through the ArgoCD UI one app at a time.
The clean answer is ApplicationSet + Git as the interface.
| |
Dev teams never touch kubectl or the ArgoCD UI for onboarding. Git is the
only interface.
Typical Enterprise Layout#
| |
The ApplicationSet watches environments/eu-dev/team-a/values/*.yaml. When a
dev team adds svc3.yaml via PR, the Application for svc3 appears without
any platform team involvement.
Where ArgoCD Projects Fit In#
Even without kubectl access, dev teams can still use the ArgoCD UI to operate their apps - check sync status, view logs, trigger a manual sync. ArgoCD Projects control what they can see and do:
- Team A can sync apps only in
team-a-project - They cannot modify or delete apps owned by other teams
- They cannot change the ApplicationSet template
The UI becomes a read/operate interface. Git is the configure interface. ApplicationSet + Projects together make self-service safe.
Decision Guide#
Use App-of-Apps when:
- You have a small, fixed set of apps that are different enough to need individual specs
- Services use mixed Helm charts, Kustomize, or plain manifests
- Dev teams need to control sync policy per app (e.g. manual sync for some, automated for others)
- Bootstrapping top-level platform components (cert-manager, ingress, ArgoCD itself)
Use ApplicationSet when:
- Many services share the same chart and differ only in configuration
- You want new services to appear automatically - just add a file to git
- Deploying the same service to multiple clusters
- A platform team manages the deployment contract; dev teams self-serve via Git
They are not mutually exclusive. A common pattern:
| |
The App-of-Apps bootstraps the few heterogeneous platform components. Each ApplicationSet handles a fleet of homogeneous services within a team boundary.
Key Takeaway#
When dev teams have no kubectl access to the argocd namespace, the UI is not
the answer - manual onboarding through the UI doesn’t scale. The right design is
ApplicationSet (for self-service via Git) + ArgoCD Projects (for
isolation and RBAC). Git PRs become the onboarding gate; the platform team only
needs to act once per team/environment, not once per service.