Pavan Rangani

HomeBlogKubernetes Multi-Cluster Management with Cluster API: Production Patterns

Kubernetes Multi-Cluster Management with Cluster API: Production Patterns

By Pavan Rangani · March 21, 2026 · DevOps & Cloud

Kubernetes Multi-Cluster Management with Cluster API: Production Patterns

Kubernetes Multi-Cluster Management with Cluster API

Kubernetes multi-cluster Cluster API (CAPI) provides a declarative, Kubernetes-native way to provision and manage clusters across any infrastructure. Instead of stitching together cloud-specific CLIs and bespoke Terraform scripts, you define clusters as Kubernetes resources and let CAPI handle provisioning, scaling, and lifecycle. In other words, it brings the same reconciliation model to cluster infrastructure that Kubernetes already brings to application workloads. As a result, a fleet of clusters becomes something you describe in YAML and review in pull requests rather than something you click through in a console.

This guide covers setting up a management cluster, provisioning workload clusters across AWS, Azure, and bare metal, driving the cluster lifecycle with GitOps, and handling day-2 operations such as upgrades and scaling. Whether you run 5 clusters or 500, the approach stays consistent — which is precisely the point. Importantly, that consistency comes at the cost of operational complexity, so we will be equally clear about when this tooling is overkill.

Cluster API Architecture

CAPI uses a dedicated management cluster to manage workload clusters. The management cluster runs the CAPI controllers, which watch for resources like Cluster, Machine, and MachineDeployment and reconcile them against real infrastructure. Each cloud has its own infrastructure provider that translates generic CAPI objects into provider-specific API calls — so the same Cluster abstraction maps onto an AWS VPC and EC2 fleet, an Azure VNet and VM scale set, or vSphere VMs, depending on which provider you install.

Kubernetes multi-cluster Cluster API architecture
CAPI architecture: management cluster provisioning workload clusters across clouds
Cluster API Architecture

Management Cluster (runs CAPI controllers):
├── CAPI Core Controller
├── Bootstrap Provider (kubeadm)
├── Control Plane Provider (kubeadm)
├── Infrastructure Provider (AWS/Azure/vSphere)
└── Watches: Cluster, Machine, MachineDeployment CRDs

Workload Cluster A (AWS):
├── 3 control plane nodes (m6i.xlarge)
├── MachineDeployment: 5 worker nodes
└── Auto-managed by CAPI controllers

Workload Cluster B (Azure):
├── 3 control plane nodes (Standard_D4s_v3)
├── MachineDeployment: 10 worker nodes
└── Auto-managed by CAPI controllers

The separation of concerns is the architecture’s defining strength. The bootstrap provider (typically kubeadm) generates the cloud-init data that turns a bare VM into a Kubernetes node; the control plane provider owns the etcd and API-server lifecycle; and the infrastructure provider owns the underlying compute and networking. Because these are independent controllers reconciling independent custom resources, you can, for example, swap kubeadm for a different bootstrap mechanism without touching how AWS networking is provisioned.

Setting Up the Management Cluster

# Initialize a management cluster
# Start with a kind cluster or existing cluster
kind create cluster --name capi-management

# Install clusterctl CLI
curl -L https://github.com/kubernetes-sigs/cluster-api/releases/latest/download/clusterctl-linux-amd64 -o clusterctl
chmod +x clusterctl && sudo mv clusterctl /usr/local/bin/

# Initialize with AWS infrastructure provider
export AWS_REGION=us-east-1
export AWS_ACCESS_KEY_ID=<your-access-key>
export AWS_SECRET_ACCESS_KEY=<your-secret-key>

clusterctl init \
  --infrastructure aws \
  --bootstrap kubeadm \
  --control-plane kubeadm

# Verify controllers are running
kubectl get pods -n capi-system
kubectl get pods -n capa-system

One pattern that production teams adopt early is the pivot. You bootstrap CAPI on a throwaway kind cluster, use it to create a small, durable management cluster on real infrastructure, and then run clusterctl move to transfer all CAPI resources to that permanent home. Doing so removes the laptop-bound kind cluster from the critical path. Furthermore, the management cluster should be hardened like any production control plane — backed-up etcd, restricted RBAC, and tightly scoped cloud credentials — because it holds the keys to every workload cluster it manages.

Provisioning a Workload Cluster

# cluster-production.yaml — Declarative cluster definition
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: production-us-east
  namespace: clusters
  labels:
    environment: production
    region: us-east-1
spec:
  clusterNetwork:
    pods:
      cidrBlocks: ["192.168.0.0/16"]
    services:
      cidrBlocks: ["10.96.0.0/12"]
    serviceDomain: cluster.local
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: production-us-east-cp
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
    kind: AWSCluster
    name: production-us-east
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: AWSCluster
metadata:
  name: production-us-east
  namespace: clusters
spec:
  region: us-east-1
  sshKeyName: capi-key
  network:
    vpc:
      cidrBlock: "10.0.0.0/16"
    subnets:
      - availabilityZone: us-east-1a
        cidrBlock: "10.0.1.0/24"
        isPublic: false
      - availabilityZone: us-east-1b
        cidrBlock: "10.0.2.0/24"
        isPublic: false
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: production-us-east-cp
  namespace: clusters
spec:
  replicas: 3
  version: v1.31.0
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
      kind: AWSMachineTemplate
      name: production-cp-template
  kubeadmConfigSpec:
    initConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: external
    clusterConfiguration:
      apiServer:
        extraArgs:
          audit-log-maxage: "30"
          audit-log-maxbackup: "10"
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: production-us-east-workers
  namespace: clusters
spec:
  clusterName: production-us-east
  replicas: 5
  selector:
    matchLabels: {}
  template:
    spec:
      clusterName: production-us-east
      version: v1.31.0
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: production-worker-config
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
        kind: AWSMachineTemplate
        name: production-worker-template

Notice that the manifest is portable in structure but not in detail. The Cluster and MachineDeployment objects are cloud-agnostic, while the AWSCluster and AWSMachineTemplate are provider-specific. Consequently, templating becomes essential the moment you manage more than a couple of clusters. The clusterctl generate cluster command and ClusterClass — a topology abstraction that lets many clusters inherit from one blueprint — both exist precisely to keep dozens of near-identical manifests from drifting apart. One important caveat: there is no network plugin in the spec above. CAPI provisions the nodes, but you must still install a CNI (Calico, Cilium, or similar) before the cluster reports Ready, which trips up newcomers who expect a turnkey result.

GitOps-Driven Cluster Lifecycle

Moreover, integrating CAPI with a GitOps controller such as Flux or Argo CD turns cluster provisioning and upgrades into reviewable, auditable Git operations. You store the cluster manifests in a repository, and the controller continuously reconciles the cluster state to match the declared intent. As a result, “create a new region” becomes a pull request, and a bad change can be rolled back the same way you roll back application code.

# flux-kustomization.yaml — GitOps for cluster management
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: clusters
  namespace: flux-system
spec:
  interval: 5m
  path: ./clusters/production
  prune: false  # Never auto-delete clusters!
  sourceRef:
    kind: GitRepository
    name: infrastructure
  healthChecks:
    - apiVersion: cluster.x-k8s.io/v1beta1
      kind: Cluster
      name: production-us-east
      namespace: clusters
  timeout: 30m  # Cluster provisioning takes time

Two settings in that manifest are load-bearing. First, prune: false is a deliberate safety measure — with pruning enabled, deleting a file in Git would tear down a live cluster, which is rarely what you want for stateful, expensive infrastructure. Second, the generous timeout reflects reality: provisioning a control plane, waiting on cloud APIs, and bootstrapping nodes routinely takes many minutes, so a short timeout produces false “failed” signals. A common refinement is to gate cluster deletion behind a separate, manual workflow so that no single merge can ever destroy production capacity.

Multi-cloud Kubernetes cluster management
Managing clusters across multiple clouds with GitOps and Cluster API

Day-2 Operations: Upgrades and Scaling

# Rolling Kubernetes upgrade — just change the version
kubectl patch kubeadmcontrolplane production-us-east-cp \
  --namespace clusters \
  --type merge \
  --patch '{"spec":{"version":"v1.32.0"}}'

# CAPI performs rolling upgrade:
# 1. Creates new control plane node with v1.32
# 2. Waits for it to be healthy
# 3. Removes old control plane node
# 4. Repeats for all control plane nodes
# 5. Upgrades workers via MachineDeployment rollout

# Scale workers
kubectl scale machinedeployment production-us-east-workers \
  --namespace clusters --replicas=10

# Monitor cluster status
kubectl get clusters -n clusters
kubectl get machines -n clusters
clusterctl describe cluster production-us-east -n clusters

Day-2 is where CAPI’s declarative model pays the largest dividend. Because upgrades happen by editing the desired version and letting controllers replace machines one at a time, you get immutable, rolling upgrades rather than risky in-place kubeadm upgrade runs on live nodes. To make this safer still, attach a MachineHealthCheck so that nodes which fail to come up healthy are automatically remediated, and always upgrade a single minor version at a time — Kubernetes does not support skipping minor versions, and CAPI will faithfully refuse to do so. For predictable cost and capacity, the cluster-autoscaler integrates with MachineDeployment objects, letting worker count track real demand instead of a static replica number.

When NOT to Use Cluster API

Cluster API adds real operational weight, and it is honest to say so. If you run fewer than a handful of clusters, managed services such as EKS, AKS, or GKE driven by Terraform are simpler and demand far less in-house expertise. Additionally, the management cluster is a meaningful single point of failure: while existing workload clusters keep running if it goes down, you cannot provision, scale, or upgrade them until it recovers — which is why backups and high availability for that cluster are not optional.

Furthermore, CAPI is a poor fit for edge or IoT scenarios where clusters sit behind NATs or on unreliable links, because the management cluster needs reliable network reach to reconcile them. The trade-off, then, is clear: CAPI rewards organizations that manage many clusters, need uniform provisioning across clouds, and staff a platform team capable of operating the management plane. As a result, smaller teams are usually better served starting with managed Kubernetes and reaching for CAPI only once multi-cluster sprawl becomes a genuine management burden rather than a hypothetical one.

Cloud infrastructure automation and management
Automating Kubernetes cluster lifecycle with Cluster API and GitOps

Key Takeaways

  • Pivot CAPI off your bootstrap kind cluster onto a hardened, backed-up management cluster
  • Use ClusterClass or templating so many clusters inherit from one reviewed blueprint
  • Remember to install a CNI — CAPI provisions nodes but does not pick your network plugin
  • Keep prune: false and gate deletions so no single merge can destroy a cluster
  • Attach MachineHealthChecks and upgrade one minor Kubernetes version at a time

For related DevOps topics, explore our guide on GitOps with Flux and ArgoCD and Terraform infrastructure automation. The Cluster API book and CAPI GitHub repository provide detailed reference documentation.

In conclusion, Kubernetes multi-cluster Cluster API brings declarative, version-controlled management to cluster infrastructure: you describe clusters as YAML, store them in Git, and let controllers reconcile provisioning and lifecycle. Combined with GitOps, the result is auditable, reproducible cluster management across any cloud. Start with a dedicated management cluster, prove the pattern on a single workload cluster, and expand only as your platform team — and your need for consistency — matures.

← Back to all articles