# One Argo CD Hub, Many Clusters, No Tickets

At most organizations, shipping an application to an environment means a Developer files a ticket, waits for the DevOps team, then verifies. The same flow repeats for a version bump, a health check port, or a replica count. Developer waiting time and DevOps workload grow together, and together they stall delivery.

The file count grows the same way. Five clusters and twenty applications, with one hand-written `Application` per pair, is one hundred files that each repeat almost everything in the file beside them. Nobody did anything wrong. The cost simply multiplies.

> This is the short version. I’ve written a full walkthrough of this architecture in [Building a Self-Service Argo CD Architecture Across Multiple Clusters](https://greennode.ai/tutorial/argocd-multi-cluster-hub-spoke-self-service?ref=blog.nh4ttruong.me).

## Five ideas

The architecture rests on five decisions, and everything else follows from them.

![](https://cdn.hashnode.com/uploads/covers/680e415fd4ea9452be36f050/3c36e1c5-69b7-420e-8803-eec4bc3cbdc4.png align="center")

*The control plane tiers. Only* `root-app-of-apps.yaml` *is applied by hand.*

*   **Hub and spoke, push model.** One Argo CD on the hub cluster holds the credentials of every spoke and pushes manifests to them.
    
*   **Repositories split by owner, not by environment.** The DevOps team owns the hub repo, developer teams own the spoke repos, and Git enforces the boundary because write access is granted per repo.
    
*   **Environments are folders, not branches.** The whole fleet follows one branch, and promotion is a file copy.
    
*   **Clusters are data.** Each cluster is a Secret carrying labels, and `ApplicationSet` joins those labels with the matching folders to generate every `Application`.
    
*   **Developers self-serve.** Add a folder, open a PR in your own repo, and the service appears in that environment. No ticket, no Argo CD access.
    

![]( align="center")

*Every cell in the grid gets generated. A directory adds a column, a labelled cluster secret adds a row.*

Clusters are labels, environments are folders, workloads are files, and the ApplicationSet is the engine that joins them.

## What the full article covers

The write-up on GreenNode goes through the mechanism behind each idea, with the YAML to back it.

*   The matrix generator that joins cluster labels with `app.yaml` files, and which template variable comes from which generator
    
*   Seven directory conventions that add an app, an environment, or a cluster without ever editing a generator
    
*   The hub and spoke repository trees, and how `AppProject` keeps developers inside a namespace
    
*   Helm wrapper charts next to Kustomize overlays, and why Argo CD itself is the one exception
    
*   Promotion as a one-line diff, and the `valueFiles` ordering that makes it work
    
*   The single sync policy that separates production from everything else
    
*   Five limits of the topology, from the missing progressive sync to the hub as a single point of failure
    
*   A demo you can run yourself, where changing one label on a cluster secret rearranges the whole fleet with no file in Git changing
    

Read it at [Building a Self-Service Argo CD Architecture Across Multiple Clusters](https://greennode.ai/tutorial/argocd-multi-cluster-hub-spoke-self-service?ref=blog.nh4ttruong.me). The code lives in [argocd-gitops-hub](https://github.com/nh4ttruong/argocd-gitops-hub) for the control plane and [argocd-gitops-spokes](https://github.com/nh4ttruong/argocd-gitops-spokes) for the workloads.
