Table of contents
- What You Don’t Know with a Single Cluster
- Registering Clusters
- Why ApplicationSet Is Needed
- List Generator — The Simplest Starting Point
- Cluster Generator — Automatic Creation from Registered Clusters
- Git Generator — Repository Structure as Configuration
- Combining Generators
What You Don’t Know with a Single Cluster
When first adopting ArgoCD, you typically install it on one cluster and deploy applications to the same cluster. The setup is simple and the behavior is easy to understand.
But as organizations grow, clusters multiply. Separate ones for development, staging, and production. They get split by region or by team. At this point, you need to decide whether to install ArgoCD separately on each cluster or manage multiple clusters from a single ArgoCD instance.
The hub-spoke model where one ArgoCD manages multiple clusters has a clear advantage: centralized management. You can see all deployment states in one place and apply policies consistently.
Registering Clusters
The cluster where ArgoCD is installed is automatically registered under the name in-cluster. To add external clusters, use the argocd cluster add command.
First, the cluster you want to add must be registered in your kubeconfig. Check the current context list.
kubectl config get-contexts
Once you’ve identified the context name of the desired cluster, register it with the ArgoCD CLI.
argocd cluster add staging-cluster-context
This command creates a argocd-manager ServiceAccount on the target cluster, sets up the necessary RBAC permissions, and stores the cluster’s connection information as a Secret in ArgoCD. You can check the registered cluster list with:
argocd cluster list
The output looks roughly like this:
SERVER NAME VERSION STATUS
https://kubernetes.default.svc in-cluster 1.28 Successful
https://10.0.1.100:6443 staging 1.28 Successful
https://10.0.2.100:6443 production 1.27 Successful
Once registration is complete, when creating an Application, specify the cluster’s API server URL in destination.server or the cluster name in destination.name.
spec:
destination:
server: https://10.0.1.100:6443
namespace: my-app
Or you can reference it by name.
spec:
destination:
name: staging
namespace: my-app
Name-based references are more readable than URLs and eliminate the need to update Application manifests when the cluster API server address changes, making the name-based approach more preferred in practice.
Why ApplicationSet Is Needed
Let’s visualize how ApplicationSet creates multiple Applications from a single template and the role Generators play.
flowchart LR
subgraph GEN["Generator (parameter source)"]
G1["List\n(explicit enumeration)"]
G2["Clusters\n(registered clusters)"]
G3["Git\n(directories/files)"]
G4["Matrix\n(cross product)"]
end
TMPL["template\n(Application template)"]
APS["ApplicationSet Controller"]
APP1["Application: app-dev"]
APP2["Application: app-staging"]
APP3["Application: app-prod"]
GEN -->|Provide parameters| APS
TMPL --> APS
APS -->|"Inject {{cluster}}"| APP1
APS -->|"Inject {{cluster}}"| APP2
APS -->|"Inject {{cluster}}"| APP3
Imagine deploying the same application to three environments (dev, staging, prod). You need three Application resources, but most of the content is identical — only the environment name and cluster address differ. This duplication becomes increasingly burdensome to manage as environments grow.
ApplicationSet was created to solve this problem. It combines a single template with a parameter source (Generator) to automatically create multiple Applications. When a new environment is added, you just add one line to the Generator’s list — making it highly scalable.
The ApplicationSet controller has been included by default since ArgoCD 2.3, so no separate installation is needed.
List Generator — The Simplest Starting Point
The List Generator lets you define the parameter list directly. It works well when you have a small, well-defined set of environments.
Let’s write an ApplicationSet that deploys the same application to three environments.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app-set
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: dev
url: https://kubernetes.default.svc
namespace: my-app-dev
- cluster: staging
url: https://10.0.1.100:6443
namespace: my-app-staging
- cluster: production
url: https://10.0.2.100:6443
namespace: my-app-prod
template:
metadata:
name: "my-app-{{cluster}}"
spec:
project: default
source:
repoURL: https://github.com/my-org/my-app.git
targetRevision: main
path: "k8s/overlays/{{cluster}}"
destination:
server: "{{url}}"
namespace: "{{namespace}}"
syncPolicy:
automated:
prune: true
selfHeal: true
Each item listed in elements is substituted into {{variable}} in the template, creating Applications. The result is three Applications: my-app-dev, my-app-staging, and my-app-prod.
It’s intuitive and easy to understand, but if environments grow to dozens, managing the list becomes tedious. In that case, it’s better to use the Cluster Generator or Git Generator.
Cluster Generator — Automatic Creation from Registered Clusters
The Cluster Generator automatically reads information from clusters registered in ArgoCD to create Applications. When a new cluster is registered, the Application appears automatically, and when a cluster is removed, the Application disappears.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: monitoring-set
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: "monitoring-{{name}}"
spec:
project: default
source:
repoURL: https://github.com/my-org/infra.git
targetRevision: main
path: monitoring/base
destination:
server: "{{server}}"
namespace: monitoring
syncPolicy:
automated:
prune: true
selfHeal: true
The selector allows you to target only clusters with specific labels. The example above deploys the monitoring stack only to clusters with the env: production label.
Labeling clusters is simple.
argocd cluster set staging --label env=staging
argocd cluster set production-us --label env=production --label region=us
argocd cluster set production-eu --label env=production --label region=eu
With this setup, monitoring is automatically deployed whenever a new env: production cluster is added. This pattern is particularly useful for infrastructure components that need to be deployed across all clusters.
Git Generator — Repository Structure as Configuration
The Git Generator creates Applications based on the directory structure or file contents of a Git repository. Add a directory to the repo and an Application appears; delete it and it disappears. The repository structure itself becomes the deployment configuration.
There are two modes: directory-based and file-based.
The directory-based mode scans directories under a specific path. Suppose you have a repo structure like this:
k8s/
├── apps/
│ ├── api-server/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ ├── web-frontend/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── worker/
│ ├── deployment.yaml
│ └── service.yaml
To create an Application for each subdirectory, write the following:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: apps-set
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/my-org/infra.git
revision: main
directories:
- path: k8s/apps/*
template:
metadata:
name: "{{path.basename}}"
spec:
project: default
source:
repoURL: https://github.com/my-org/infra.git
targetRevision: main
path: "{{path}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{path.basename}}"
syncPolicy:
automated:
prune: true
selfHeal: true
{{path.basename}} becomes the directory name (api-server, web-frontend, worker), and {{path}} becomes the full path (k8s/apps/api-server, etc.). When adding a new microservice, just creating a directory automatically sets up the deployment.
The file-based mode reads parameters from JSON or YAML files. Use this when more granular configuration is needed.
config/
├── api-server.json
├── web-frontend.json
└── worker.json
If api-server.json contains the following:
{
"appName": "api-server",
"namespace": "api",
"cluster": "https://kubernetes.default.svc",
"replicas": "3"
}
These values can be referenced in the template.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: apps-from-files
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/my-org/infra.git
revision: main
files:
- path: config/*.json
template:
metadata:
name: "{{appName}}"
spec:
project: default
source:
repoURL: https://github.com/my-org/infra.git
targetRevision: main
path: "k8s/apps/{{appName}}"
destination:
server: "{{cluster}}"
namespace: "{{namespace}}"
syncPolicy:
automated:
prune: true
selfHeal: true
Since each JSON file corresponds to one Application, you can manage each app’s configuration independently while keeping a single template.
Combining Generators
Generators are powerful on their own, but combining multiple ones handles more complex scenarios. The Matrix Generator creates a Cartesian product of two Generators’ results.
For example, if you need to deploy 3 apps to 3 clusters, that’s 9 Applications total. The Matrix Generator lets you express this concisely.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: matrix-set
namespace: argocd
spec:
generators:
- matrix:
generators:
- git:
repoURL: https://github.com/my-org/infra.git
revision: main
directories:
- path: k8s/apps/*
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: "{{path.basename}}-{{name}}"
spec:
project: default
source:
repoURL: https://github.com/my-org/infra.git
targetRevision: main
path: "{{path}}"
destination:
server: "{{server}}"
namespace: "{{path.basename}}"
syncPolicy:
automated:
prune: true
selfHeal: true
The Git Generator produces the app list, and the Cluster Generator produces the cluster list — Applications are created for every combination of the two. When new apps or clusters are added, it scales automatically, significantly reducing operational burden.
Managing multiple clusters from a central point and automating repetitive tasks with ApplicationSet noticeably improves infrastructure operations efficiency. But as clusters and applications grow, “who can access what” becomes important. In the next part, we’ll learn how to configure team-level access control through ArgoCD’s RBAC settings and SSO integration.


Loading comments...