# Argo CD ApplicationSet Controller: Multi-Cluster GitOps Automation Explained

> Discover the Argo CD ApplicationSet controller for automated multi-cluster GitOps. Automate Argo CD Application reconciliation with generators, templates, and sync policies.

- Repository: [Argo Project/argo-cd](https://github.com/argoproj/argo-cd)
- Tags: deep-dive
- Published: 2026-07-09

---

**The Argo CD ApplicationSet controller is a Kubernetes controller that watches the `ApplicationSet` custom resource and automatically reconciles a fleet of Argo CD `Application` objects using generators, templates, and progressive sync policies.**

The Argo CD ApplicationSet controller, housed in the `argoproj/argo-cd` repository, extends Argo CD’s capabilities by enabling declarative management of multiple Application resources across clusters and environments. Unlike the standard Application controller which manages individual deployments, this controller implements a reconcile loop that generates, validates, and synchronizes entire sets of applications based on dynamic data sources such as Git repositories, cluster lists, or SCM providers.

## How the Argo CD ApplicationSet Controller Works

The controller implements a standard Kubernetes controller pattern through a five-stage reconcile loop defined in [`applicationset/controllers/applicationset_controller.go`](https://github.com/argoproj/argo-cd/blob/main/applicationset/controllers/applicationset_controller.go):

1. **Read the `ApplicationSet` spec** to extract generator configurations and templates.
2. **Generate desired `Application` resources** by invoking configured generators and executing `template.GenerateApplications` from [`applicationset/template/template.go`](https://github.com/argoproj/argo-cd/blob/main/applicationset/template/template.go).
3. **Validate generated applications** against project existence, destination validity, and duplicate application names.
4. **Reconcile the current cluster state** by creating new applications, updating changed ones via `createOrUpdateInCluster`, and deleting stale resources.
5. **Update the `ApplicationSet` status** using `updateResourcesStatus` and `setAppSetApplicationStatus` to reflect resource health, progressive sync progress, and error conditions.

## Core Architecture and Implementation

### Reconciliation Logic in applicationset_controller.go

The primary entry point is the `Reconcile(ctx, req)` method in [`applicationset/controllers/applicationset_controller.go`](https://github.com/argoproj/argo-cd/blob/main/applicationset/controllers/applicationset_controller.go). This method drives the entire lifecycle process, handling resource deletion, progressive synchronization, and status migration. It manages complex scenarios such as invalid destinations through `removeFinalizerOnInvalidDestination`, which ensures finalizers are cleaned up when target clusters become unreachable, preventing deletion deadlocks.

### Generator Plugin System

The controller delegates application generation to pluggable generators located in `applicationset/generators/`. Each generator—whether **List**, **Git**, **Cluster**, or **SCM**—produces parameter sets that the controller passes to `template.GenerateApplications`. This architecture allows the ApplicationSet to dynamically create Application manifests based on external data sources, such as discovering directories in a Git repository or listing available clusters from cluster secrets.

### Progressive Sync and Rollout Management

The experimental **progressive sync** feature, implemented in `applicationset/progressivesync`, enables phased rollouts of applications across clusters. When the controller starts with the `--enable-progressive-syncs` flag, it executes the steps defined in `spec.progressiveSync` (such as `pause` or `sync`) sequentially rather than simultaneously. The controller updates the `ApplicationSet` status with rollout progress, allowing for canary-style deployments or maintenance windows.

### Ownership and Finalizer Management

The controller establishes strict ownership relationships by adding the ApplicationSet as an `OwnerReference` to each generated Application. This ensures Kubernetes garbage collection removes dependent applications when the parent ApplicationSet is deleted. The finalizer logic guarantees safe deletion even when destination clusters become invalid, using `removeFinalizerOnInvalidDestination` to clean up resources that can no longer reach their targets.

## Deployment and Operational Configuration

### Controller Flags and Runtime Options

The controller runs as the binary `argocd-applicationset-controller`, typically deployed as a separate pod (`argocd-applicationset-controller`) with its own ServiceAccount and RBAC bindings. Key operational flags include:

- **`--concurrent-application-updates`**: Controls parallelism for application modifications (default varies by version).
- **`--enable-progressive-syncs`**: Activates the experimental progressive rollout feature.
- **`--metrics-addr`**: Exposes Prometheus metrics via `ApplicationsetMetrics` (default `:8080`).

Documentation for these flags is maintained in [`docs/operator-manual/server-commands/argocd-applicationset-controller.md`](https://github.com/argoproj/argo-cd/blob/main/docs/operator-manual/server-commands/argocd-applicationset-controller.md).

### RBAC and Kubernetes Resources

Deployment manifests reside in `manifests/base/applicationset-controller/`, defining the controller’s Deployment, ServiceAccount, Role, and ClusterRole bindings. The Custom Resource Definition for the ApplicationSet API, including all generator and sync policy specifications, is defined in [`manifests/crds/applicationset-crd.yaml`](https://github.com/argoproj/argo-cd/blob/main/manifests/crds/applicationset-crd.yaml). Detailed specification documentation is available in [`docs/operator-manual/applicationset/applicationset-specification.md`](https://github.com/argoproj/argo-cd/blob/main/docs/operator-manual/applicationset/applicationset-specification.md).

## Practical Implementation Examples

### Basic List Generator Configuration

The following manifest uses the **list generator** to create two Application resources—one for development and one for production—from a single template:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook
spec:
  generators:
  - list:
      elements:
      - cluster: dev
        url: https://k8s.dev.example.com
      - cluster: prod
        url: https://k8s.prod.example.com
  template:
    metadata:
      name: '{{cluster}}-guestbook'
    spec:
      project: default
      source:
        repoURL: https://github.com/argoproj/guestbook
        path: helm
        targetRevision: HEAD
      destination:
        server: '{{url}}'
        namespace: guestbook

```

The controller processes the list elements and generates two separate Application objects, substituting `{{cluster}}` and `{{url}}` with the respective values.

### Progressive Sync with Rolling Strategy

To enable phased rollouts across multiple applications, configure the `progressiveSync` field and ensure the controller runs with `--enable-progressive-syncs`:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: rolling-demo
spec:
  syncPolicy:
    syncOptions:
    - CreateNamespace=true
  progressiveSync:
    steps:
    - pause: {}
    - sync: {}
  generators:
  - git:
      repoURL: https://github.com/example/applications
      revision: main
      directories:
      - path: '*'
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/example/applications
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: default

```

This configuration pauses before syncing each application discovered in the Git repository, allowing for manual verification or automated checks between steps.

### Running the Controller Locally

For development or debugging outside of cluster deployments, execute the controller directly with specific flags:

```bash

# Start the controller inside the cluster (usually launched by the Helm chart)

argocd-applicationset-controller \
  --loglevel info \
  --concurrent-application-updates 5 \
  --enable-progressive-syncs \
  --metrics-addr :8080

```

This command exposes metrics on port 8080 and limits concurrent updates to five applications to prevent API server throttling.

## Summary

- The **Argo CD ApplicationSet controller** automates the creation and management of multiple Application resources from a single declarative specification, turning high-level descriptions into concrete Argo CD applications.
- It implements a reconcile loop in [`applicationset/controllers/applicationset_controller.go`](https://github.com/argoproj/argo-cd/blob/main/applicationset/controllers/applicationset_controller.go) that generates applications via pluggable generators, validates them against project and destination constraints, and reconciles cluster state.
- The controller supports advanced rollout strategies through **progressive sync**, managed via the `applicationset/progressivesync` package, when enabled with the `--enable-progressive-syncs` flag.
- It handles resource ownership through Kubernetes **owner references** and **finalizers** to ensure safe deletion and prevent orphaned resources.
- Deployed as the `argocd-applicationset-controller` binary with dedicated RBAC, it operates independently from the main Argo CD server and exposes metrics via `ApplicationsetMetrics`.

## Frequently Asked Questions

### What is the difference between Application and ApplicationSet in Argo CD?

An **Application** is a single deployable unit targeting one specific cluster and source repository, while an **ApplicationSet** is a parent resource that generates and manages multiple Application objects across clusters or environments. The ApplicationSet controller watches the ApplicationSet CR and reconciles the desired state of the underlying Application fleet, enabling template-based multi-cluster deployments.

### How does the ApplicationSet controller handle application deletion?

When an ApplicationSet is deleted, the controller uses Kubernetes **owner references** to ensure all generated Application resources are garbage collected. Additionally, the `removeFinalizerOnInvalidDestination` function in [`applicationset_controller.go`](https://github.com/argoproj/argo-cd/blob/main/applicationset_controller.go) removes finalizers from applications when their target clusters become unreachable, preventing deletion deadlocks and ensuring clean resource removal.

### What generators are available in the Argo CD ApplicationSet controller?

The controller supports multiple generators defined in `applicationset/generators/`, including the **List generator** (for static parameter sets), **Git generator** (for directories or files in repositories), **Cluster generator** (for discovered clusters from secrets), and **SCM generator** (for pull requests in source control providers). Each generator produces parameter sets that feed into the Application template rendering process.

### Is progressive sync production-ready in Argo CD?

Progressive sync remains an **experimental feature** as implemented in the `applicationset/progressivesync` package. It requires explicitly enabling the `--enable-progressive-syncs` flag on the `argocd-applicationset-controller` binary and defining rollout steps in the `spec.progressiveSync` field. Check the official Argo CD documentation and release notes for current maturity status before deploying in production environments.