Overview
With generators, the ApplicationSet controller provides powerful tools to automate the templating and modification of Argo CD Applications. Here are the primary use cases that ApplicationSet was designed to support.
Cluster Add-ons
An initial design focus of the ApplicationSet controller was to enable infrastructure teams to automatically create and manage a large set of Argo CD Applications across many clusters as a single unit.
The Problem
Infrastructure teams are responsible for provisioning cluster add-ons to multiple Kubernetes clusters:
Cluster add-ons are operators and controllers like:
Prometheus operator for metrics
Argo Workflows controller for workflow orchestration
Cert-manager for certificate management
Ingress controllers
Scale challenges :
Organizations may manage tens, hundreds, or thousands of clusters
New clusters are added/modified/removed regularly
Different add-ons need to target different cluster subsets (staging vs production)
Permission requirements :
Installing add-ons requires cluster-level permissions
Development teams (cluster tenants) don’t have these permissions
Infrastructure/ops teams need automation to scale
The Solution
The infrastructure team maintains a Git repository with application manifests for cluster add-ons. ApplicationSet automatically deploys these add-ons to target clusters.
Option 1: List Generator
Manually maintain a list of target clusters:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : prometheus-operator
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- list :
elements :
- cluster : engineering-dev
url : https://dev.example.com
- cluster : engineering-prod
url : https://prod.example.com
template :
metadata :
name : '{{.cluster}}-prometheus'
spec :
project : cluster-addons
source :
repoURL : https://github.com/infra-team/cluster-addons.git
targetRevision : HEAD
path : prometheus-operator
destination :
server : '{{.url}}'
namespace : monitoring
syncPolicy :
automated :
prune : true
selfHeal : true
syncOptions :
- CreateNamespace=true
Adding/removing clusters requires manually updating the ApplicationSet’s list elements.
Option 2: Cluster Generator (Recommended)
Automatically detect all clusters defined in Argo CD:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : prometheus-operator
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- clusters :
selector :
matchLabels :
environment : production
template :
metadata :
name : '{{.name}}-prometheus'
spec :
project : cluster-addons
source :
repoURL : https://github.com/infra-team/cluster-addons.git
targetRevision : HEAD
path : prometheus-operator
destination :
server : '{{.server}}'
namespace : monitoring
syncPolicy :
automated :
prune : true
selfHeal : true
syncOptions :
- CreateNamespace=true
Adding/removing a cluster from Argo CD automatically creates/removes the corresponding Application.
Option 3: Git Generator
Most flexible approach using Git as the source of truth:
Using files field:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : cluster-addons
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- git :
repoURL : https://github.com/infra-team/cluster-config.git
revision : HEAD
files :
- path : "clusters/**/config.json"
template :
metadata :
name : '{{.cluster.name}}-prometheus'
spec :
project : cluster-addons
source :
repoURL : https://github.com/infra-team/cluster-addons.git
targetRevision : HEAD
path : prometheus-operator
destination :
server : '{{.cluster.address}}'
namespace : monitoring
clusters/prod-us-east/config.json:
{
"cluster" : {
"name" : "prod-us-east" ,
"address" : "https://prod.us-east.example.com"
}
}
Using directories field:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : cluster-addons
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- git :
repoURL : https://github.com/infra-team/cluster-config.git
revision : HEAD
directories :
- path : clusters/*
template :
metadata :
name : '{{.path.basename}}-prometheus'
spec :
project : cluster-addons
source :
repoURL : https://github.com/infra-team/cluster-addons.git
targetRevision : HEAD
path : prometheus-operator
destination :
name : '{{.path.basename}}'
namespace : monitoring
Monorepos
In the monorepo use case, cluster administrators manage the entire state of a Kubernetes cluster from a single Git repository.
The Problem
Single Git repository contains manifests for multiple applications
Changes to the repository should automatically deploy to the cluster
Need to avoid creating individual Application resources for each service manually
Want GitOps workflow where merging to main triggers deployment
The Solution
ApplicationSet automatically discovers applications in the monorepo and creates Application resources for each.
Git Directory Approach
Repository structure:
cluster-config/
├── argo-workflows/
│ ├── deployment.yaml
│ └── service.yaml
├── prometheus-operator/
│ └── values.yaml
├── cert-manager/
│ └── kustomization.yaml
└── backend-services/
├── api/
│ └── deployment.yaml
└── worker/
└── deployment.yaml
ApplicationSet:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : cluster-apps
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- git :
repoURL : https://github.com/org/cluster-config.git
revision : HEAD
directories :
- path : "*"
- path : "backend-services/*"
template :
metadata :
name : '{{.path.basename}}'
spec :
project : default
source :
repoURL : https://github.com/org/cluster-config.git
targetRevision : HEAD
path : '{{.path.path}}'
destination :
server : https://kubernetes.default.svc
namespace : '{{.path.basename}}'
syncPolicy :
automated :
prune : true
selfHeal : true
syncOptions :
- CreateNamespace=true
This creates Applications for argo-workflows, prometheus-operator, cert-manager, api, and worker.
Git Files Approach
Repository structure:
apps/
├── argo-workflows/
│ ├── config.json
│ └── manifests/
├── prometheus/
│ ├── config.json
│ └── manifests/
└── cert-manager/
├── config.json
└── manifests/
apps/argo-workflows/config.json:
{
"appName" : "argo-workflows" ,
"namespace" : "workflows" ,
"syncPolicy" : "automated"
}
ApplicationSet:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : cluster-apps
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- git :
repoURL : https://github.com/org/cluster-config.git
revision : HEAD
files :
- path : "apps/**/config.json"
template :
metadata :
name : '{{.appName}}'
spec :
project : default
source :
repoURL : https://github.com/org/cluster-config.git
targetRevision : HEAD
path : '{{.path.path}}/manifests'
destination :
server : https://kubernetes.default.svc
namespace : '{{.namespace}}'
syncPolicy :
automated :
prune : true
selfHeal : true
Self-Service Multi-Tenant Deployments
The self-service use case allows developers on multi-tenant Kubernetes clusters to deploy applications without requiring cluster administrator intervention.
The Problem
Developers want to:
Deploy multiple applications to a single cluster
Deploy to multiple clusters
Do so without involving cluster administrators for each deployment
Traditional app-of-apps approach issues:
Developers define Argo CD Application resources in Git
Admins review/accept via merge requests
Security risk : Application spec contains sensitive fields:
project - controls permissions
cluster - target cluster
namespace - target namespace
Inadvertent merge could grant excessive permissions
The Solution
ApplicationSet allows admins to restrict sensitive fields while letting developers customize safe fields.
Safe Self-Service Pattern
Repository structure (developer-controlled):
apps/
├── team-a/
│ ├── frontend/
│ │ └── config.json
│ └── backend/
│ └── config.json
└── team-b/
└── api/
└── config.json
apps/team-a/frontend/config.json:
{
"app" : {
"source" : "https://github.com/team-a/frontend" ,
"revision" : "v1.2.3" ,
"path" : "k8s"
}
}
ApplicationSet (admin-controlled):
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : team-apps
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- git :
repoURL : https://github.com/org/app-configs.git
revision : main
files :
- path : "apps/**/config.json"
template :
metadata :
name : '{{.path.segments 1}}-{{.path.basename}}' # team-a-frontend
spec :
project : '{{.path.segments 1}}' # RESTRICTED: project based on team folder
source :
# CUSTOMIZABLE: developers control these via config.json
repoURL : '{{.app.source}}'
targetRevision : '{{.app.revision}}'
path : '{{.app.path}}'
destination :
name : production-cluster # RESTRICTED: admins control target cluster
namespace : '{{.path.segments 1}}' # RESTRICTED: namespace based on team folder
syncPolicy :
automated :
prune : true
selfHeal : true
Security benefits:
Developers can customize: source repo, revision, path
Admins control: target cluster, namespace, project
Developers cannot escalate privileges
Git commits only contain config.json, not full Application specs
Multi-Cluster Self-Service
Extend to multiple clusters using Matrix generator:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : team-apps-multi-cluster
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- matrix :
generators :
# Developer-controlled app configs
- git :
repoURL : https://github.com/org/app-configs.git
revision : main
files :
- path : "apps/**/config.json"
# Admin-controlled cluster list
- list :
elements :
- cluster : staging
url : https://staging.example.com
- cluster : production
url : https://production.example.com
template :
metadata :
name : '{{.path.segments 1}}-{{.path.basename}}-{{.cluster}}'
spec :
project : '{{.path.segments 1}}'
source :
repoURL : '{{.app.source}}'
targetRevision : '{{.app.revision}}'
path : '{{.app.path}}'
destination :
server : '{{.url}}'
namespace : '{{.path.segments 1}}'
This creates Applications for each app config on each cluster: team-a-frontend-staging, team-a-frontend-production, etc.
Multi-Cluster Deployment Patterns
Deploy Different Apps to Different Clusters
Use cluster labels to control which apps deploy where:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : selective-deployment
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- matrix :
generators :
- git :
repoURL : https://github.com/org/apps.git
revision : HEAD
files :
- path : "apps/**/config.json"
- clusters :
selector :
matchLabels :
argocd.argoproj.io/secret-type : cluster
app-tier : '{{.tier}}' # Matches tier from config.json
template :
metadata :
name : '{{.name}}-{{.appName}}'
spec :
project : default
source :
repoURL : '{{.source}}'
targetRevision : HEAD
path : '{{.path}}'
destination :
server : '{{.server}}'
namespace : '{{.namespace}}'
Progressive Rollout Across Clusters
Deploy to dev, then staging, then production:
apiVersion : argoproj.io/v1alpha1
kind : ApplicationSet
metadata :
name : progressive-rollout
spec :
goTemplate : true
goTemplateOptions : [ "missingkey=error" ]
generators :
- clusters :
selector :
matchLabels :
environment : dev
values :
revision : main
- clusters :
selector :
matchLabels :
environment : staging
values :
revision : release-candidate
- clusters :
selector :
matchLabels :
environment : production
values :
revision : stable
template :
metadata :
name : '{{.name}}-myapp'
spec :
project : default
source :
repoURL : https://github.com/org/myapp.git
targetRevision : '{{.values.revision}}'
path : k8s
destination :
server : '{{.server}}'
namespace : myapp
Best Practices
Start Simple with List/Cluster Generators
Begin with List or Cluster generators before moving to more complex Git-based generators. This helps you understand the fundamentals.
Use Automated Sync Policies
Enable automated sync with prune and selfHeal for true GitOps: syncPolicy :
automated :
prune : true
selfHeal : true
Use cluster labels extensively for flexible targeting:
environment: production/staging/dev
region: us-east/us-west/eu
tier: frontend/backend/data
Control Sensitive Fields in Self-Service
Always hard-code project, destination.server, and destination.namespace when allowing developer customization.
Use Matrix for Combinations
When you need to deploy multiple apps to multiple clusters, Matrix generator eliminates duplication.
Next Steps
Generators Dive deep into each generator type and their configuration options
Security Understand security implications and access control for ApplicationSets