Cluster Federation

Cluster federation was Kubernetes’ first answer to the multi-cluster problem: a control plane above the clusters that presented one API for managing many. Two generations were built and both abandoned. Federation v1 (early codename “Ubernetes”, ~2016) bolted a federation API server in front of a set of member clusters and re-implemented federated versions of core resources; it never reached maturity. Federation v2, productized as KubeFed, took a cleaner CRD-based approach — a host cluster running KubeFed controllers, wrapped types like FederatedDeployment and FederatedConfigMap that embed a template plus placement (which clusters) and overrides (per-cluster diffs) (Kubernetes Blog — Federation Evolution). KubeFed too failed to gain traction, and on January 3rd, 2023 SIG-Multicluster archived the project (SIG Multicluster — Archiving Kubefed); the repository now lives at kubernetes-retired/kubefed. This note is therefore largely historical: it explains what federation tried to be, why the wrapped-type model proved clunky, and what replaced the idea — GitOps-to-many-clusters (ArgoCD ApplicationSet, Flux), Karmada, and Open Cluster Management. Understanding the failure is valuable precisely because every later multi-cluster tool is, in part, a reaction to KubeFed’s mistakes.

Mental Model

The mental model of federation is a meta-cluster that owns federated objects, with a controller that fans those objects out to member clusters. The federation control plane does not run workloads — it runs federation controllers that read federated resources and write plain Kubernetes resources into each selected member cluster’s API. The defining (and ultimately fatal) design choice of KubeFed was the wrapped type: rather than reuse the existing Deployment API, KubeFed defined a new CRD, FederatedDeployment, whose body is {template, placement, overrides} — the template being a Deployment spec embedded as opaque JSON.

flowchart TB
    USER["Operator"] -->|kubectl apply| HOST
    subgraph HOST["Host Cluster (KubeFed control plane)"]
        FD["FederatedDeployment<br/>{ template, placement, overrides }"]
        FC["FederatedConfigMap"]
        KFCTL["KubeFed controllers<br/>(sync controller)"]
    end
    FD --> KFCTL
    FC --> KFCTL
    KFCTL -->|"template + override A"| MA
    KFCTL -->|"template + override B"| MB
    KFCTL -->|"template (no override)"| MC
    subgraph MA["Member Cluster A — placement: yes"]
        DA["plain Deployment<br/>replicas: 5"]
    end
    subgraph MB["Member Cluster B — placement: yes"]
        DB["plain Deployment<br/>replicas: 2 (override)"]
    end
    subgraph MC["Member Cluster C — placement: NO"]
        NONE["nothing — not selected"]
    end

What this shows. The host cluster holds one FederatedDeployment. The KubeFed sync controller reads it, evaluates placement (clusters A and B are selected, C is not), applies overrides per cluster (B gets replicas: 2 instead of the template’s 5), and writes a plain Deployment into A and B. The insight to extract is the source of the friction: a user who wants to federate a workload they already have as a Deployment cannot just label it — they must rewrite it as a FederatedDeployment, manually nesting their existing spec under .spec.template.spec.template. Every resource type needs a corresponding Federated* wrapper. Existing tooling (Helm charts, kubectl plugins, IDE schemas) does not understand the wrapped types. This is the clunkiness that doomed the model — and the exact thing Karmada later fixed by letting you propagate vanilla Kubernetes resources.

Mechanical Walk-through

Federation v1 (“Ubernetes”). The earliest attempt put a federation API server (a near-clone of kube-apiserver) in front of member clusters, with a federated controller-manager re-implementing replication, ingress, and service controllers at the federation level. It had its own etcd, its own resource types, and required clients to talk to it instead of the member clusters. It was effectively a second, parallel Kubernetes implementation — heavy, hard to keep in sync with upstream API evolution, and limited to a handful of resource kinds. It was deprecated without ever graduating.

Federation v2 / KubeFed. The redesign abandoned the parallel-API-server model for a CRD + controller model that ran inside an ordinary cluster (the host cluster). KubeFed’s pieces:

  • Host cluster. An ordinary Kubernetes cluster that runs the KubeFed controllers and stores the Federated* CRD instances. It is not special infrastructure — it is just a cluster with KubeFed installed.
  • Member clusters. Clusters joined to the federation via kubefedctl join, which creates a KubeFedCluster object (holding the member’s API endpoint and credentials) in the host cluster.
  • Type configuration. A FederatedTypeConfig object told KubeFed “federation is enabled for resource kind X” and pointed at the generated FederatedX CRD. KubeFed shipped FederatedTypeConfigs for the common kinds and kubefedctl enable could generate more.
  • Federated resources. For each enabled kind, a wrapped CRD: FederatedDeployment, FederatedConfigMap, FederatedSecret, FederatedService, FederatedNamespace, etc. The body has three parts:
    • template — the embedded spec of the underlying resource (a Deployment spec, verbatim, nested).
    • placement — which member clusters this resource targets, by explicit clusters list or by clusterSelector label match.
    • overrides — per-cluster JSON-patch-style modifications (e.g., “in cluster B set /spec/replicas to 2”).
  • Sync controller. The core loop: watch Federated* objects, compute per-cluster desired state (template + that cluster’s override), and reconcile a plain resource into each placed member cluster. It also propagated status back.
  • Scheduling (ReplicaSchedulingPreference). A ReplicaSchedulingPreference object could distribute a federated workload’s total replica count across clusters by weight, with rebalancing.

The control flow: an operator creates a FederatedDeployment in the host cluster; the sync controller resolves placement, applies overrides, and writes a Deployment into each selected member; member clusters’ own controllers then run the workload as normal; the sync controller watches the members and reflects aggregate status back into the federated object.

Why KubeFed failed. The reasons, distilled from the archival announcement and SIG-Multicluster discussion:

  1. The wrapped-type model was clunky. Every workflow that worked on a Deployment broke on a FederatedDeployment. Helm charts, kubectl rollout, kubectl scale, admission policies, CI manifest validation, IDE YAML schemas — all of them speak the standard types and none of them understood KubeFed’s wrappers. Adopting KubeFed meant rewriting your manifests and re-tooling your pipeline.
  2. Type coverage was a treadmill. Each new resource kind — and especially every CRD a user wanted to federate — needed a FederatedTypeConfig and a generated wrapper. Keeping pace with the expanding Kubernetes API and the CRD explosion was a losing race.
  3. The host cluster was a single point of failure and a single blast radius — the very problem multi-cluster is supposed to solve.
  4. GitOps ate the use case. While KubeFed languished, GitOps tools matured. ArgoCD’s ApplicationSet with a cluster generator, and Flux, could fan a standard manifest set out to N clusters from Git — no wrapped types, full compatibility with existing tooling, and Git as an auditable source of truth. For the config-propagation slice of multi-cluster, GitOps was simply better.
  5. The community split toward lighter tools. SIG-Multicluster concluded (CNCF blog) that the multi-cluster problem space was too broad for one monolithic federation tool, and that Karmada and Open Cluster Management — narrower, more composable, and (in Karmada’s case) wrapper-free — better served real needs.

KubeFed was archived on January 3rd, 2023, in an announcement co-signed by SIG-Multicluster chairs Jeremy Olmsted-Thompson and Paul Morie (SIG Multicluster — Archiving Kubefed). Two details from that announcement matter for accurate framing: (1) archived means frozen, not deleted — the code remains at kubernetes-retired/kubefed for reference or forking, but receives no further fixes or releases. (2) The SIG explicitly did not endorse a successor: “we don’t have a SIG-endorsed replacement project and will not be linking to other projects from the tombstone.” The community has since converged on Karmada and OCM, but that convergence was bottom-up, not a SIG anointing. SIG-Multicluster’s own current subprojects are narrower API specifications — the About API, the Multicluster Services API, the Work API, and the ClusterProfile API (multicluster.sigs.k8s.io) — each addressing one specific facet of multi-cluster (identification, service discovery, manifest distribution, cluster inventory) rather than re-attempting a monolithic federation.

Configuration / API Surface

A representative KubeFed FederatedDeployment — shown for historical literacy, not for current use:

apiVersion: types.kubefed.io/v1beta1          # KubeFed API group — project archived
kind: FederatedDeployment                     # the WRAPPED type — not a plain Deployment
metadata:
  name: web
  namespace: commerce
spec:
  template:                                   # the embedded standard Deployment spec
    metadata:
      labels: {app: web}
    spec:
      replicas: 5                             # the DEFAULT replica count for all clusters
      selector:
        matchLabels: {app: web}
      template:                               # ...and here is ANOTHER nested template — the Pod
        metadata:
          labels: {app: web}
        spec:
          containers:
            - name: web
              image: registry.example.com/web:1.4.2
  placement:                                  # WHICH member clusters get this resource
    clusters:
      - name: cluster-us-east
      - name: cluster-eu-west
  overrides:                                  # PER-CLUSTER modifications
    - clusterName: cluster-eu-west
      clusterOverrides:
        - path: "/spec/replicas"              # JSON-pointer into the template
          value: 2                            # eu-west runs 2 replicas, not 5

Line-by-line: kind: FederatedDeployment is the friction in one word — this is not a Deployment, so nothing that operates on Deployment recognizes it. .spec.template holds the entire ordinary Deployment spec verbatim, which is why the YAML has the awkward template.spec.template double nesting (the outer template is KubeFed’s; the inner is the Deployment’s Pod template). .spec.placement.clusters enumerates target member clusters (a clusterSelector with label matching was the alternative). .spec.overrides carries per-cluster JSON-pointer patches — here eu-west’s replica count is dropped to 2. To enable federation for a kind in the first place you needed a FederatedTypeConfig, and members were joined with kubefedctl join <cluster> --host-cluster-context <host>.

The modern replacement for this exact intent — fan one workload to many clusters with per-cluster tweaks — is a Karmada PropagationPolicy + OverridePolicy over a plain Deployment, or an ArgoCD ApplicationSet. Neither requires a wrapped type. See those notes for current YAML.

Failure Modes

These are the failure modes that killed the project, recorded as cautionary history:

Tooling incompatibility. The wrapped types were invisible to Helm, Kustomize, kubectl subcommands, admission webhooks, and schema validators. Adoption meant abandoning or re-plumbing the existing delivery pipeline — a cost most teams refused to pay.

Type-coverage lag. A resource (especially a CRD) could not be federated until a FederatedTypeConfig and wrapper existed for it. Federated tooling chronically trailed the API surface it was supposed to manage.

Host-cluster fragility. Losing the host cluster lost federated management of the entire fleet — concentrating, rather than dispersing, blast radius. The control plane above the clusters was itself an unfederated single cluster.

Status-aggregation lag and conflicts. Reflecting member-cluster status back into federated objects was eventually consistent and could conflict if a resource was edited directly in a member cluster, producing confusing drift between the federated view and reality.

Stalled maintenance. As contributors moved to Karmada and OCM, KubeFed accumulated unfixed bugs and stale dependencies well before the formal 2023 archival — running it in production meant running unmaintained software.

Alternatives and When to Choose Them

Because KubeFed is archived, every alternative is “the thing to use instead”:

GitOps fan-out — ArgoCD ApplicationSet / Flux. For the config-propagation use case (deploy the same standard manifests to N clusters, with per-cluster parameterization), GitOps is the modern default. ApplicationSet with a cluster generator templates one Application per registered cluster; manifests stay standard; Git is the auditable source of truth. This is what most teams reach for first.

Karmada. The direct spiritual successor for workload orchestration. Karmada deliberately fixes KubeFed’s central mistake: you write vanilla Kubernetes resources and attach a separate PropagationPolicy / OverridePolicy to control placement and per-cluster diffs — no Federated* wrappers, full tooling compatibility. Karmada is a CNCF project (incubating) under active development. Note that “SIG-endorsed successor” is not an accurate framing — the SIG-Multicluster archival announcement explicitly declines to anoint a replacement; the CNCF blog calling Karmada and OCM “new approaches to the multi-cluster challenge” is a community-level survey, not a SIG endorsement.

Open Cluster Management (OCM). The other prominent community direction (CNCF blog): a hub-and-spoke model with a ManifestWork API for pushing resources to managed clusters and a Placement API for cluster selection. Backs Red Hat’s Advanced Cluster Management for Kubernetes.

The Multi-Cluster Services API. Under SIG-Multicluster’s own stewardship, the MCS API specifies the service-discovery slice (ServiceExport/ServiceImport) and is orthogonal to all of the above — it does not move workloads, it lets a Service in one cluster be resolvable from another. See Multi-Cluster Kubernetes.

When to choose federation-the-pattern at all: only when workloads or config genuinely must span clusters. Many fleets need no federation — independent clusters, each GitOps-reconciled from its own repo, is simpler and sufficient.

Production Notes

KubeFed never achieved meaningful production penetration; it spent its whole life in beta-ish maturity and was archived before reaching GA. Treat any tutorial, blog post, or course module that presents KubeFed as a current multi-cluster solution as out of date — the project is frozen at kubernetes-retired/kubefed and SIG-Multicluster explicitly redirects users to its current subprojects (Karmada, OCM, the MCS API, About API).

The enduring lesson — and the reason this note exists in Kubernetes MOC §17 despite covering a dead project — is a design principle that recurs throughout Kubernetes: do not invent a parallel type system when you can attach policy to the existing one. KubeFed’s FederatedDeployment is the canonical anti-example; Karmada’s “plain Deployment + separate PropagationPolicy” is the canonical fix; the same principle shows up in the Gateway API’s role-split design and in Crossplane’s later move away from heavy abstraction layers. An ecosystem will route around a tool that forces it to abandon its existing tooling.

Uncertain

Federation v1’s precise deprecation timeline is fuzzy in surviving documentation — it was wound down gradually around 2017–2018 rather than at a single announced date. The 2018 Kubernetes blog “Federation Evolution” post is the closest thing to an official mid-flight retrospective; the v1 codebase (kubernetes/federation) was eventually removed from the main Kubernetes monorepo, but the exact PR/release for that physical removal is not surfaced by current upstream docs. Treat any specific v1 deprecation date as approximate unless a primary commit/PR can be cited. The KubeFed (v2) archival date — January 3rd, 2023 — is not uncertain: it is stated directly in the SIG-Multicluster announcement.

See Also

  • Kubernetes MOC — parent map
  • Multi-Cluster Kubernetes — the umbrella; federation was one (failed) approach to it
  • Karmada — the actively-developed community alternative; “vanilla resources, no wrapped types”
  • Cluster APIcluster-lifecycle sibling: federation tried to coordinate workloads across clusters; CAPI creates the clusters themselves. Often paired with Karmada (CAPI creates, Karmada places)
  • ArgoCD — GitOps fan-out (ApplicationSet) — what replaced federation’s config-propagation role
  • Flux — sibling GitOps tool, same role
  • Cloud Native Computing Foundation — the graduation tiers that distinguish Karmada (active CNCF project) from KubeFed (archived)
  • Custom Resource Definition — the mechanism KubeFed’s wrapped types were built on
  • Deployment — the standard type KubeFed wrapped, and the type Karmada uses directly
  • Kubernetes Cluster Architecture — the single-cluster topology federation tried to multiply