Scaling GitOps: Using Argo CD ApplicationSets to Eliminate Manifest Duplication
Stop duplicating Argo CD Application manifests. Learn how to use ApplicationSets and generators to automate multi-cluster deployments from a single template.
25 Sept 2025, 20:34 UTC

Managing a handful of Argo CD Applications is straightforward, but as you scale to dozens of clusters or hundreds of namespaces, the "one manifest per app" approach fails. You end up with a repository full of nearly identical YAML files where the only differences are the destination server and namespace. This creates massive review noise and high risk of configuration drift.
The solution is the ApplicationSet. Instead of manually defining every Application, you define a template and a generator. The ApplicationSet controller then automatically renders and manages the child Applications based on the data provided by the generator.
How Generators Drive the Fleet
An ApplicationSet is a Custom Resource Definition (CRD) managed by a controller that watches for changes in your source of truth. It uses Generators to produce a list of parameters, which are then injected into a Go-template to create standard Argo CD Application objects.
- Git Generator: Scans a repository for directories or files, allowing you to add a new application to your fleet simply by adding a folder to Git.
- Cluster Generator: Queries the Argo CD API for clusters that match specific labels, enabling a "fan-out" deployment to all clusters in a specific region or environment.
- List Generator: Uses a hardcoded list of values for smaller, static sets of environments.
- Matrix Generator: Combines two other generators (e.g., Git x Cluster) to create a Cartesian product of applications across clusters.
Worked Example: Multi-Cluster App Discovery
Consider a scenario where you have a mono-repo containing several microservices under an apps/ directory. You want every service to be deployed to every cluster labeled env: production.
Run the following manifest in the argocd namespace with cluster-admin permissions. Replace https://github.com/example/mono.git with your actual repository URL.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: prod-fleet-apps
namespace: argocd
spec:
generators:
- matrix:
generators:
- git:
repoURL: https://github.com/example/mono.git
revision: main
directories:
- path: apps/*
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: '{{ .path.basename }}-{{ .cluster.name }}'
spec:
project: default
source:
repoURL: https://github.com/example/mono.git
targetRevision: main
path: '{{ .path }}'
destination:
server: '{{ .cluster.server }}'
namespace: '{{ .path.basename }}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
In this configuration, the Matrix Generator pairs every directory found in apps/* with every cluster matching the label env: production. If you have 5 apps and 3 clusters, Argo CD will automatically generate 15 Application resources. If you add a 6th app directory to Git, the controller detects the change and creates 3 new Applications without any manual YAML updates.
Operational Constraints and Risks
ApplicationSets introduce a layer of abstraction that can lead to "resource explosions." If a generator is too broad (e.g., targeting all clusters without a label filter), you may accidentally trigger hundreds of deployments, putting significant load on the Argo CD API server and the Kubernetes API servers of the target clusters.
Additionally, the ApplicationSet controller is often a separate component from the core Argo CD server. Depending on your installation method (Helm, Operator, or Manifests), you must ensure the controller is active and the CRDs are installed.
Verifying the Deployment
To verify the ApplicationSet is functioning, use kubectl from a workstation with access to the argocd namespace:
# Check if the CRD is installed
kubectl get crd applicationsets.argoproj.io
# List all ApplicationSets
kubectl get applicationset -n argocd
# Inspect the generated child applications
kubectl get applications -n argocd -l app.kubernetes.io/instance=prod-fleet-apps
The expected result is a list of Applications following the {{ .path.basename }}-{{ .cluster.name }} naming convention. If no Applications appear, check the ApplicationSet controller logs for generator errors or authentication failures with the Git repository.
Conclusion
ApplicationSets shift the engineering effort from managing individual manifests to designing a scalable delivery pattern. By combining Git and Cluster generators, you can maintain a lean repository while managing a complex fleet. To avoid instability, always use specific label selectors in your cluster generators and treat the resulting child Applications as read-only objects managed by the controller.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.