ApplicationSet in Argo CD: Scaling GitOps Without Manifest Duplication
Stop copy-pasting Application manifests. Use Argo CD ApplicationSet generators to create Applications dynamically, with a practical example and operational caveats.
06 Oct 2025, 00:15 UTC

The copy-paste tax in multi-cluster GitOps
When you run Argo CD across a handful of clusters, the standard Application resource works fine. You write one manifest per cluster, point it at a Git path, and move on. But when the estate grows to dozens of clusters and environments, that manual copy-paste becomes a tax. Every change—an image tag bump, a values tweak, a new repo path—means editing many near-identical files. Drift creeps in. Reviewers lose signal because the diff is mostly boilerplate.
ApplicationSet: one template, many Applications
ApplicationSet is a controller that ships with Argo CD (since 2.1) and removes that duplication. Instead of writing N Application manifests, you write one ApplicationSet manifest. Inside it, a generator produces parameters, and a template turns those parameters into concrete Application resources. Argo CD then reconciles each generated Application exactly as if you had applied it by hand.
The most common generators are:
- List: a static array of values you define inline. Good for known environments or clusters.
- Git: discovers directories or files in a repository, so adding a folder automatically creates an Application.
- Cluster: selects Argo CD registered clusters by label, which is the natural fit for true multi-cluster fleets.
Worked example: environments with distinct Helm values
The following ApplicationSet uses a List generator to create three Applications—one each for dev, staging, and prod—from a single spec. Each gets its own Helm values file.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: platform-apps
namespace: argocd
spec:
generators:
- list:
elements:
- name: dev
valuesFile: values-dev.yaml
- name: staging
valuesFile: values-staging.yaml
- name: prod
valuesFile: values-prod.yaml
template:
metadata:
name: '{{name}}-myapp'
spec:
project: default
source:
repoURL: https://github.com/example/platform.git
targetRevision: main
path: charts/myapp
helm:
valueFiles:
- '{{valuesFile}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{name}}'
syncPolicy:
automated:
prune: true
selfHeal: true
Apply it to the cluster where Argo CD runs:
kubectl apply -f applicationset.yaml -n argocd
Within moments, the ApplicationSet controller creates three Applications: dev-myapp, staging-myapp, and prod-myapp. You can confirm them in the UI or with:
kubectl get applications -n argocd
For an actual multi-cluster deployment, swap the List generator for a Cluster generator that selects clusters by label. The template stays almost identical; only the destination.server field changes to reference the generated cluster URL.
Trade-offs and operational limits
ApplicationSet is powerful, but it is not free.
Controller load. Generating hundreds of Applications from one spec concentrates reconciliation work in the ApplicationSet controller. If you generate thousands of resources, sync waves and controller memory can become bottlenecks. Start with a moderate number and watch the controller pod metrics.
RBAC exposure. The Cluster generator reads Argo CD's cluster secrets. If your RBAC grants broad read access to the argocd namespace, users may see cluster URLs and metadata they should not. Scope roles tightly so that only platform operators can read cluster registration data.
Version floor. The controller and CRD arrived in Argo CD 2.1. On earlier releases, the API server rejects the resource. If you are on an older version, upgrade before adopting this pattern.
Template rigidity. The Go-template syntax is concise but unforgiving. A missing {{name}} placeholder or an incorrect values path fails silently at generation time and shows up only in controller logs, not as a failed Application sync.
Verification and next steps
If you installed Argo CD via Helm, ensure the ApplicationSet controller is enabled; the upstream chart exposes a flag such as --set applicationSet.enabled=true. Then verify the controller is running:
kubectl get deploy -n argocd | grep applicationset
Apply a small, non-critical ApplicationSet and inspect the controller logs:
kubectl logs -n argocd deploy/argocd-applicationset-controller -f
Look for lines confirming that parameters were substituted and Applications were created or updated. If nothing appears, check that the generator actually returned elements—an empty List or a Git path with no directories produces no Applications and no obvious error.
Once the pattern is proven, migrate existing duplicated Applications into ApplicationSets incrementally. Keep the old manifests until the generated replacements are healthy, then remove them. For multi-cluster fleets, adopt the Cluster generator with label-based selection and restrictive RBAC from day one, rather than exposing cluster metadata broadly.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.