Argo CD ApplicationSet for multi‑cluster GitOps without per‑cluster Application YAML
Use Argo CD ApplicationSet to generate many Applications from one template driven by Git data. Covers minimal design, trust boundaries, operational checks, failure modes and when to split the design.
20 Oct 2025, 13:58 UTC

The problem is manual Application sprawl
Operating Argo CD across many clusters or tenants creates a lot of near‑identical Application resources. One template for a workload, many targets with different parameters. The useful takeaway is to let Argo CD generate those Applications declaratively from Git data with ApplicationSet, and keep the control plane small and auditable.
Requirements for a template‑driven fleet
A design using ApplicationSet must satisfy:
- Declarative generation of many Applications from one template with per‑target parameters.
- Git as the source of truth for both the template and the generator data.
- No secrets or cluster credentials in plain Git.
- RBAC boundaries that prevent one tenant or cluster from writing into another.
- Observability into generation and sync, not just the final Application health.
ApplicationSet is a controller that watches an ApplicationSet custom resource and creates, updates, or deletes child Applications according to generators and a template. Generators such as list, cluster, git, or SCM evaluate inputs from Git or the API server and produce a set of parameters.
Smallest suitable design
The minimal working topology is three parts: Argo CD control plane with ApplicationSet controller enabled, a Git repository holding the template and generator data, and a single ApplicationSet resource that references them.
Git repo layout:
apps/
base/
deployment.yaml
generators/
clusters.yaml
applicationset.yaml
Example ApplicationSet with a list generator. Run kubectl with read access to the argocd namespace.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: workload-per-cluster
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: cluster-a
url: https://
namespace: prod
- cluster: cluster-b
url: https://
namespace: prod
template:
metadata:
name: '{{cluster}}-workload'
namespace: argocd
spec:
project:
source:
repoURL:
targetRevision: main
path: apps/base
helm:
parameters:
- name: clusterName
value: '{{cluster}}'
destination:
server: '{{url}}'
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true
This creates one Application per element. The controller reconciles the ApplicationSet and ensures the child Applications exist with the mapped parameters. No manual Application YAML per cluster is required.
Trust and data boundaries
Git is authenticated to Argo CD via repo credentials stored in the Argo CD secret store. The ApplicationSet controller runs in the control plane and has access to the Git repo and the Kubernetes API for the Argo CD instance. It does not need cluster admin credentials.
Generated Applications inherit the Argo CD project RBAC scope defined in spec.project. If all Applications share one project, they share the same RBAC. Splitting projects is the primary way to enforce tenant isolation.
Secrets must not be templated in generator data. Use external secret management and reference secrets via Argo CD project secrets or external tools. Cluster credentials are managed by Argo CD cluster secrets, not by ApplicationSet.
Data boundary: Git contains intent only. The controller is the only writer of child Applications in the argocd namespace. Target clusters are written only by Argo CD Application controllers using the credentials bound to each Application.
Operational checks
Check generation health, not just sync health.
- ApplicationSet status. In the Argo CD UI, open the ApplicationSet resource and verify the list of generated Applications matches the generator input.
- Controller reconciliation. Check ApplicationSet controller logs in the argocd namespace for successful reconciliation and generator evaluation messages. Look for restarts and resource usage.
- Drift between generator output and actual Applications. Compare Git commit history for changes to generators/clusters.yaml with ApplicationSet status and child Application sync state.
- Repo sync status for the ApplicationSet itself. A failing repo connection stalls generation.
Example read‑only check, run from a workstation with kubectl access to the Argo CD cluster:
kubectl get applicationset -n argocd workload-per-cluster -o yaml
kubectl get applications -n argocd -l applicationset=workload-per-cluster
Required permissions are get/list in the argocd namespace. Risks: broad read access can expose target cluster URLs and project names.
Failure modes
Generator source unavailable. If the Git repo is unreachable, the ApplicationSet controller keeps the last known Applications. Changes are not propagated until the repo is reachable.
Invalid template parameters. A bad value in the generator data causes Application creation or update failures. The ApplicationSet status surfaces errors per generated Application.
ApplicationSet controller outage. No new Applications are created or removed, and updates to existing Applications are not generated. Argo CD continues to manage existing Applications.
RBAC misconfiguration. A project allows writes to unintended clusters or namespaces. Generated Applications can push manifests to the wrong destination.
Generator type and feature differences are version sensitive. ApplicationSet features and generator options vary across Argo CD releases.
When the design should change
Change the design when boundaries or data requirements exceed a single ApplicationSet.
- Dynamic secrets per target. Move to an Application of Applications pattern or separate Argo CD instances with per‑team repo credentials.
- Cross‑repository generators. Use multiple ApplicationSets with distinct repo credentials or a central generator repo with read‑only access.
- Per‑team isolation with admin boundaries. Split by Argo CD project and namespace, or use separate Argo CD instances.
- Multi‑admin boundaries where teams must not see each other's generator data. Separate ApplicationSets and restrict Git access.
Limitations: generated Applications share the Argo CD project and RBAC scope unless explicitly split per team or cluster. ApplicationSet does not manage secrets. It is a generation layer, not a policy engine.
Practical verification after change: inspect the UI for the ApplicationSet, confirm the generated Application count matches the generator elements, and review Git history against ApplicationSet status and child Application sync state.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.