Running one cluster is hard enough. Running the same application consistently across multiple clusters — different regions, different clouds, or a mix of both — introduces an entirely new category of problem: keeping configuration, policy, and placement in sync without managing each cluster by hand. Kubernetes Federation was the project’s first serious attempt at solving this, and its history matters for anyone evaluating it today.
A Necessary History Lesson: KubeFed’s Status
The original Kubernetes Federation project (v1, then v2/KubeFed) was developed under SIG Multicluster but has been effectively unmaintained for a long time — KubeFed development wound down and it never graduated to a stable, generally-recommended GA status. Anyone searching “Kubernetes Federation” today needs to know this up front, because much of what’s written about it describes a project that isn’t the active recommended path anymore. This article covers KubeFed’s concepts (since they’re genuinely useful for understanding the problem space) and then covers the tools that are actually recommended for multi-cluster management today.
The Problem Federation Was Trying to Solve
- Deploying the same workload consistently to multiple clusters.
- Keeping ConfigMaps, Secrets, and RBAC policy synchronized across clusters.
- Placement rules — e.g., “run this Deployment in the EU cluster and the US cluster, but not the APAC one.”
- A single API surface to manage resources across an entire fleet rather than one cluster at a time.
KubeFed Concepts (Historical Reference)
KubeFed introduced Federated resource types wrapping ordinary Kubernetes objects with per-cluster overrides:
apiVersion: types.kubefed.io/v1beta1
kind: FederatedDeployment
metadata:
name: myapp
namespace: production
spec:
template:
metadata:
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
spec:
containers:
- name: myapp
image: registry.example.com/myapp:1.0.0
placement:
clusters:
- name: cluster-us-east
- name: cluster-eu-west
overrides:
- clusterName: cluster-eu-west
clusterOverrides:
- path: "/spec/replicas"
value: 5
This is worth understanding conceptually even though KubeFed itself isn’t the recommended production path anymore — the “template + placement + per-cluster overrides” model reappears in the tools that replaced it.
What’s Actually Recommended Today: Argo CD ApplicationSets
For GitOps-style multi-cluster deployment, Argo CD’s ApplicationSet controller is the modern, actively maintained equivalent, using generators to fan a single template out across a cluster list:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp
namespace: argocd
spec:
generators:
- clusters: {}
template:
metadata:
name: '{{name}}-myapp'
spec:
project: default
source:
repoURL: https://github.com/example/manifests.git
targetRevision: main
path: production
destination:
server: '{{server}}'
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
kubectl apply -f myapp-applicationset.yaml -n argocd
kubectl -n argocd get applications
NAME SYNC STATUS HEALTH STATUS
cluster-us-myapp Synced Healthy
cluster-eu-myapp Synced Healthy
Registering additional clusters with Argo CD:
argocd cluster add cluster-eu-west-context --name cluster-eu-west
Cluster Registration and Access
Argo CD (like KubeFed before it) needs credentials to reach each member cluster’s API server. This is managed via kubeconfig contexts and Argo CD Secrets:
apiVersion: v1
kind: Secret
metadata:
name: cluster-eu-west
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
name: cluster-eu-west
server: https://eu-west-cluster-api.example.com
config: |
{
"bearerToken": "...",
"tlsClientConfig": {
"insecure": false,
"caData": "..."
}
}
Service Mesh Alternative: Multi-Cluster Networking
A related but distinct problem is networking across clusters (as opposed to deploying across clusters) — service meshes like Istio and Cilium’s ClusterMesh address this by letting Services in one cluster discover and call Services in another directly:
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: multicluster
spec:
values:
global:
meshID: mesh1
multiCluster:
clusterName: cluster-us-east
network: network1
This solves a genuinely different problem than ApplicationSets/KubeFed — cross-cluster service discovery and traffic routing, rather than keeping deployed resources in sync — and the two are often used together in serious multi-cluster architectures.
RBAC Across Clusters
Multi-cluster tooling needs to be granted access per member cluster, scoped as tightly as single-cluster RBAC would be:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argocd-manager
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: argocd-manager-binding
subjects:
- kind: ServiceAccount
name: argocd-manager
namespace: kube-system
roleRef:
kind: ClusterRole
name: argocd-manager
apiGroup: rbac.authorization.k8s.io
In practice, scope this down per-organization rather than granting blanket wildcard access — the example above mirrors Argo CD’s own default bootstrap role but should be tightened for anything beyond initial evaluation.
Placement Strategies
Real multi-cluster placement decisions typically factor in:
- Data residency — EU customer data must stay on EU clusters.
- Latency — serve from the geographically nearest cluster.
- Capacity — spread load where clusters have headroom.
- Blast radius — avoid deploying a risky change to all clusters simultaneously; canary one cluster first.
ApplicationSet generators support list-based, cluster-label-based, and Git-directory-based placement, letting these strategies be expressed declaratively rather than hardcoded per deployment.
Monitoring a Multi-Cluster Fleet
Federating observability itself is its own challenge — Thanos or Grafana Mirror/Cortex are the common patterns for aggregating Prometheus metrics across clusters into a single queryable view:
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: main
namespace: monitoring
spec:
thanos:
objectStorageConfig:
key: thanos.yaml
name: thanos-objstore-config
kubectl -n argocd get applicationsets
kubectl -n argocd get applications -l app.kubernetes.io/instance=myapp
Why Teams End Up Needing Multi-Cluster At All
It’s worth being explicit about the actual drivers, since “just use one big cluster” is often the right answer and multi-cluster complexity shouldn’t be adopted casually:
- Blast radius isolation — a control-plane incident or a bad cluster-wide change (a broken CRD upgrade, a misconfigured admission webhook) affects one cluster, not the entire fleet.
- Regulatory data residency — some jurisdictions require customer data to physically remain within a specific region’s infrastructure, which a single global cluster generally can’t satisfy on its own.
- Cloud provider diversification — running the same workload across two cloud providers as insurance against a provider-wide outage or pricing changes.
- Scale limits — even though modern Kubernetes clusters can handle thousands of nodes, some organizations still find operational value in splitting by team, region, or environment rather than pushing one cluster to its practical ceiling.
Multi-cluster tooling is the answer to a real problem, but it also multiplies operational surface area — every cluster needs its own upgrade cadence, its own monitoring, its own on-call familiarity — so it’s worth confirming the driver above is a genuine requirement before adopting it purely because it’s available.
Disaster Recovery as a Placement Strategy
Beyond routine multi-region deployment, a common and often underappreciated use of multi-cluster placement is disaster recovery itself — keeping a warm or cold standby cluster ready to receive traffic if the primary region becomes unavailable:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp-dr
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: cluster-primary
replicas: "5"
- cluster: cluster-dr-standby
replicas: "1"
template:
metadata:
name: '{{cluster}}-myapp'
spec:
source:
repoURL: https://github.com/example/manifests.git
targetRevision: main
path: production
helm:
parameters:
- name: replicaCount
value: '{{replicas}}'
destination:
server: '{{cluster}}'
namespace: production
The standby cluster runs at reduced capacity (replicas: 1) continuously, staying warm and ready, and a failover event (DNS cutover, load balancer reweighting) simply redirects traffic and scales the standby cluster up — a meaningfully faster recovery path than provisioning a cluster from scratch after an incident has already begun.
Alternative: Config Sync for Policy-Heavy Multi-Cluster Fleets
Argo CD’s ApplicationSets are the most common choice for application deployment across a fleet, but for organizations whose primary multi-cluster need is enforcing consistent policy and configuration — RBAC baselines, NetworkPolicies, resource quotas — rather than application rollout specifically, Google’s Config Sync (part of Anthos Config Management, also usable independently of GKE) takes a slightly different angle, treating an entire Git repository as the source of truth for cluster configuration and continuously reconciling every registered cluster against it:
apiVersion: configsync.gke.io/v1beta1
kind: RootSync
metadata:
name: root-sync
namespace: config-management-system
spec:
sourceFormat: unstructured
git:
repo: https://github.com/example/cluster-config.git
branch: main
dir: "clusters/production"
auth: none
The conceptual overlap with Argo CD ApplicationSets is real — both are pull-based reconciliation against Git — but Config Sync leans more heavily toward policy and baseline configuration as its primary use case, while Argo CD’s ecosystem (Applications, ApplicationSets, Rollouts) is more oriented around application deployment workflows specifically. Some organizations run both side by side: Config Sync for cluster-wide policy baselines every cluster must satisfy, and Argo CD for the actual application rollouts layered on top of that baseline.
Evaluating Whether a Team Is Actually Ready for Multi-Cluster
Before adopting any of the tooling covered in this article, it’s worth an honest gut-check: does the team already have solid single-cluster operational practices — monitoring, RBAC discipline, tested backup/restore, a working CI/CD pipeline — genuinely in place? Multi-cluster tooling multiplies whatever operational maturity already exists; it doesn’t substitute for it. A team that hasn’t yet nailed reliable single-cluster operations will generally find that adopting multi-cluster federation on top of that gap multiplies the underlying problems across every cluster in the fleet, rather than solving them. The tooling in this article is genuinely valuable once the actual driver (data residency, blast-radius isolation, disaster recovery, geographic latency) is real and well understood — it’s considerably less valuable when adopted speculatively, ahead of any concrete requirement, purely because it’s available.
Common Mistakes
- Adopting KubeFed today, unaware that it’s not an actively maintained, generally-recommended path anymore — leading to a hard-to-support dead end.
- Conflating multi-cluster deployment tooling (ApplicationSets, KubeFed) with multi-cluster networking tooling (service mesh) — they solve different problems and aren’t substitutes for each other.
- Granting overly broad cross-cluster RBAC to the management tooling “to make it work,” creating a single compromised credential capable of affecting every cluster in the fleet at once.
- Rolling out a change to all clusters simultaneously instead of canarying one cluster first, turning a config mistake into a multi-region incident.
Summary
Kubernetes Federation, as originally conceived (KubeFed), isn’t the recommended path for multi-cluster management today — the concepts it introduced (templates, placement, per-cluster overrides) live on more successfully in tools like Argo CD’s ApplicationSets, with service meshes handling the separate problem of cross-cluster networking. For anyone starting multi-cluster work now, ApplicationSets plus a service mesh (if cross-cluster service calls are actually needed) is the more supportable, actively maintained combination.
References
- Argo CD ApplicationSet documentation: https://argo-cd.readthedocs.io/en/stable/user-guide/application-set/
- KubeFed project (historical/archival reference): https://github.com/kubernetes-retired/kubefed
- Istio multi-cluster documentation: https://istio.io/latest/docs/setup/install/multicluster/
- Cilium ClusterMesh: https://docs.cilium.io/en/stable/network/clustermesh/
- CNCF multi-cluster landscape: https://landscape.cncf.io/