ArgoCD Multi-Cluster App of Apps
Manage multiple Kubernetes clusters with ArgoCD App of Apps, deploying shared infrastructure and cluster-specific workloads from a single GitOps repository.
π‘ Quick Answer: Create a parent App of Apps per cluster, each pointing to a cluster-specific directory in Git. Use a shared
base/for common infrastructure andclusters/<name>/for cluster-specific overrides.
The Problem
Managing multiple Kubernetes clusters (dev, staging, production, multi-region) requires:
- Shared infrastructure deployed consistently across all clusters
- Cluster-specific configuration β different replicas, resources, feature flags
- Centralized management from a single ArgoCD instance
- Drift prevention β all clusters match their Git-defined state
The Solution
Repository Structure
gitops-repo/
βββ apps/
β βββ base/ # Shared across all clusters
β β βββ cert-manager.yaml
β β βββ ingress-nginx.yaml
β β βββ monitoring.yaml
β βββ clusters/
β β βββ dev/ # Dev cluster apps
β β β βββ _cluster.yaml # Cluster-specific root app
β β β βββ base-apps.yaml # References base/ apps
β β β βββ dev-workloads.yaml
β β βββ staging/
β β β βββ _cluster.yaml
β β β βββ base-apps.yaml
β β β βββ staging-workloads.yaml
β β βββ production/
β β βββ _cluster.yaml
β β βββ base-apps.yaml
β β βββ prod-workloads.yaml
βββ workloads/
β βββ base/ # Shared workload manifests
β β βββ api/
β βββ overlays/
β βββ dev/
β βββ staging/
β βββ production/Step 1: Register Target Clusters
# Add clusters to ArgoCD
argocd cluster add dev-cluster --name dev
argocd cluster add staging-cluster --name staging
argocd cluster add prod-us-east --name production-us-east
argocd cluster add prod-eu-west --name production-eu-west
# Verify clusters
argocd cluster listOr declaratively:
apiVersion: v1
kind: Secret
metadata:
name: prod-us-east-cluster
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
name: production-us-east
server: https://k8s-prod-us-east.example.com
config: |
{
"bearerToken": "<service-account-token>",
"tlsClientConfig": {
"insecure": false,
"caData": "<base64-ca-cert>"
}
}Step 2: Global Root App of Apps
# root-app.yaml β Creates one App of Apps per cluster
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: global-root
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: main
path: apps/clusters
directory:
recurse: false # Only scan cluster directories
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: trueStep 3: Per-Cluster App of Apps
# apps/clusters/production/_cluster.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: production-cluster
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0"
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: main
path: apps/clusters/production
directory:
exclude: "_cluster.yaml" # Don't recurse into self
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: trueStep 4: Shared Infrastructure Per Cluster
# apps/clusters/production/base-apps.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prod-cert-manager
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "-3"
spec:
project: default
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.16.0
helm:
values: |
installCRDs: true
replicaCount: 3 # Production: higher replicas
destination:
server: https://k8s-prod-us-east.example.com # Target cluster!
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true# apps/clusters/dev/base-apps.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dev-cert-manager
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "-3"
spec:
project: default
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.16.0
helm:
values: |
installCRDs: true
replicaCount: 1 # Dev: single replica
destination:
server: https://k8s-dev.example.com # Dev cluster
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueStep 5: Cluster-Specific Workloads
# apps/clusters/production/prod-workloads.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prod-api
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: main
path: workloads/overlays/production
destination:
server: https://k8s-prod-us-east.example.com
namespace: myapp
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueArchitecture
flowchart TD
A[Global Root App] --> B[Dev Cluster Apps]
A --> C[Staging Cluster Apps]
A --> D[Production Cluster Apps]
B -->|"server: dev"| B1[cert-manager]
B -->|"server: dev"| B2[dev-workloads]
C -->|"server: staging"| C1[cert-manager]
C -->|"server: staging"| C2[staging-workloads]
D -->|"server: prod"| D1[cert-manager Γ3]
D -->|"server: prod"| D2[monitoring HA]
D -->|"server: prod"| D3[prod-workloads]Common Issues
Cluster Credentials Expired
# Check cluster connectivity
argocd cluster list
# Refresh credentials
argocd cluster add prod-cluster --name production --upsertApplication Name Conflicts
Each child Application needs a unique name across all clusters:
# Prefix with cluster name
name: prod-cert-manager # Not just "cert-manager"
name: dev-cert-managerBest Practices
- Prefix app names with cluster β
prod-redis,dev-redisto avoid conflicts - Use Kustomize overlays for cluster-specific configs β same base manifests, different parameters
- Separate Projects per environment β RBAC isolation between dev/staging/prod
- Use cluster labels β
environment: production,region: us-eastfor filtering - Pin chart versions per environment β staging gets new versions before production
- Monitor from management cluster β centralized ArgoCD dashboard shows all clusters
Key Takeaways
- App of Apps scales to multi-cluster by creating per-cluster root applications
- Each child Application targets a specific cluster via
destination.server - Share infrastructure definitions but customize replicas, resources per environment
- One Git repo, one ArgoCD instance, all clusters managed declaratively

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
