Cluster API introduces a fundamental chicken-and-egg paradox:
If Kubernetes manages Kubernetes clusters declaratively through Custom Resource Definitions and controllers, what cluster runs the controllers that create the very first management cluster?
You cannot provision a high-availability, production-grade management cluster in AWS, Azure, or GCP without an active Kubernetes control plane running CAPI controllers (capi-controller-manager, capa-controller-manager, etc.). Yet spinning up that permanent cloud cluster requires an operational API server.
Cluster API resolves this paradox through the Bootstrap and Pivot pattern.
An operator creates a temporary, ephemeral bootstrap cluster (typically running locally via KinD). That bootstrap cluster provisions the permanent management cluster in the target cloud. Once the cloud cluster is healthy, Cluster API executes a pivot: transferring the entire Custom Resource graph and reconciliation authority from the ephemeral cluster to the permanent cluster using clusterctl move. Finally, the ephemeral bootstrap cluster is destroyed.
1. The Bootstrap Cluster vs. Management Cluster Separation
To master the pivot workflow, contrast the three types of clusters in a Cluster API ecosystem:
| Dimension | Bootstrap Cluster | Management Cluster | Workload Cluster |
|---|---|---|---|
| Lifespan | Ephemeral (minutes to hours; destroyed after pivot) | Long-lived (months to years; permanent infrastructure plane) | Dynamic (created, upgraded, scaled, and deleted on demand) |
| Hosting Environment | Local developer laptop or ephemeral CI runner (KinD / Minikube) | Production cloud or bare-metal environment (HA control plane) | Cloud VPCs, on-prem datacenters, or edge locations |
| Primary Workload | Initial CAPI controllers and bootstrap CRD graph | CAPI controllers, GitOps engines, cluster telemetry | Business applications, databases, stateful user services |
| Reliability Tier | Single-node, disposable, non-HA | Multi-AZ, 3+ control plane nodes, automated etcd backups | Sized according to application SLAs |
| Lifecycle Anchor | Created via Docker/local VM runtime | Provisioned declaratively by the bootstrap cluster | Provisioned declaratively by the management cluster |
2. The Architectural Mental Model: What Moves During a Pivot?
A common point of operational confusion is misunderstanding what clusterctl move actually migrates:
flowchart TD
subgraph PhaseBefore["Phase A: Before Pivot"]
BC_A["Bootstrap Cluster (KinD on Laptop/CI)"]
MC_A["Permanent Cloud Management Cluster<br/>(VMs, VPC, Load Balancer in AWS/Azure)"]
BC_A -->|"Hosts CAPI CRD Graph & Reconciles"| MC_A
end
subgraph PhasePivot["Phase B: clusterctl move"]
TRANS["clusterctl move transfers:<br/>• Cluster, KCP, MachineDeployments<br/>• Provider CRDs (AWSCluster, AWSMachine)<br/>• Secrets (Certificates, Tokens, Credentials)"]
end
subgraph PhaseAfter["Phase C: After Pivot"]
BC_C["Bootstrap Cluster<br/>(Deleted via kind delete)"]
MC_C["Permanent Cloud Management Cluster<br/>(Self-Managing or Managing Fleet)"]
WC_C["Workload Clusters"]
MC_C -->|"Reconciles its own nodes & workload fleet"| WC_C
end
PhaseBefore --> PhasePivot
PhasePivot --> PhaseAfter
The Fundamental Rule:
clusterctl movenever moves physical or cloud infrastructure. The virtual machines, networks, storage volumes, and control plane endpoints remain running untouched in the cloud. What moves is the declarative management state: the etcd custom resource records, owner references, and reconciliation authority.
3. The 8-Stage Bootstrap and Pivot Lifecycle
Transitioning from an empty workstation to a production management cluster follows an eight-stage sequence:
flowchart TD
S1["1. Create Ephemeral KinD Cluster<br/>(kind create cluster --name capi-bootstrap)"] --> S2["2. Initialize CAPI Providers on KinD<br/>(clusterctl init --infrastructure aws)"]
S2 --> S3["3. Apply Cloud Management Cluster Manifest<br/>(kubectl apply -f management-cluster.yaml)"]
S3 --> S4["4. Await Infrastructure & Control Plane Stability<br/>(clusterctl describe cluster ...)"]
S4 --> S5["5. Extract Target Admin Kubeconfig<br/>(clusterctl get kubeconfig ...)"]
S5 --> S6["6. Initialize CAPI Providers on Target Cluster<br/>(clusterctl init --kubeconfig target.kubeconfig)"]
S6 --> S7["7. Execute clusterctl move<br/>(Transfers CRD graph & pauses/resumes reconciliation)"]
S7 --> S8["8. Teardown Ephemeral KinD Cluster<br/>(kind delete cluster --name capi-bootstrap)"]
Detailed Execution Walkthrough
Stage 1: Ephemeral Bootstrap Cluster
Spin up a local containerized Kubernetes cluster:
kind create cluster --name capi-bootstrap
Stage 2: Initialize Bootstrap Providers
Install the Core, Bootstrap, Control Plane, and Infrastructure provider controllers onto the KinD cluster:
clusterctl init --infrastructure aws
Stage 3: Provision the Future Management Cluster
Generate and apply the manifest describing the target management cluster. The target cluster must include worker nodes (or untainted control plane nodes) to host the CAPI controller manager pods after the move:
clusterctl generate cluster prod-mgmt \
--kubernetes-version v1.31.0 \
--control-plane-machine-count 3 \
--worker-machine-count 2 \
> prod-mgmt.yaml
kubectl apply -f prod-mgmt.yaml
Stage 4: Verify Stability
Wait until the control plane reports Initialized = True, all machines report Running, and nodes report Ready = True:
clusterctl describe cluster prod-mgmt
Stage 5: Extract Target Kubeconfig
Retrieve the admin credentials for the newly created cloud cluster:
clusterctl get kubeconfig prod-mgmt > prod-mgmt.kubeconfig
Stage 6: Initialize Providers on the Target Cluster
Before migrating objects, the target cluster must have identical (or newer) versions of all CAPI CRDs and controller webhooks installed:
clusterctl init \
--kubeconfig prod-mgmt.kubeconfig \
--infrastructure aws
Stage 7: Execute clusterctl move
Transfer the custom resource graph:
# Always execute dry-run first to inspect discovered resources
clusterctl move \
--to-kubeconfig prod-mgmt.kubeconfig \
--dry-run -v 5
# Execute the live migration
clusterctl move \
--to-kubeconfig prod-mgmt.kubeconfig
Stage 8: Teardown Bootstrap Cluster
Verify that the target management cluster is actively reconciling its own resources, then delete the local KinD cluster:
kind delete cluster --name capi-bootstrap
4. Under the Hood of clusterctl move
The clusterctl move command is not a simple kubectl get -o yaml | kubectl apply. It implements a specialized, multi-phase migration protocol designed to prevent split-brain dual-reconciliation storms:
sequenceDiagram
autonumber
participant CLI as clusterctl CLI
participant SrcAPI as Source API (Bootstrap)
participant Cloud as Cloud Infrastructure (AWS/Azure)
participant TgtAPI as Target API (Management)
participant TgtCtrl as Target CAPI Controllers
CLI->>SrcAPI: 1. Discovers full CRD graph via owner references
CLI->>SrcAPI: 2. Sets Cluster.spec.paused = true (Split-Brain Prevention)
Note over SrcAPI,Cloud: Source controllers freeze reconciliation loops
CLI->>SrcAPI: 3. Exports CRDs, Secrets (CAs, tokens), ConfigMaps
CLI->>TgtAPI: 4. Applies resources to target cluster
CLI->>TgtAPI: 5. Verifies all objects established and webhooks validate
CLI->>SrcAPI: 6. Deletes objects from source (suppressing infrastructure deletion)
CLI->>TgtAPI: 7. Sets Cluster.spec.paused = false
TgtCtrl->>Cloud: 8. Target controllers adopt instances & rebuild status from reality
Phase 1: Dependency Graph Discovery
clusterctl identifies all root Cluster resources in the target namespace and recursively traverses their ownerReferences. It builds an in-memory graph containing Cluster, InfrastructureCluster, KubeadmControlPlane, MachineDeployment, MachineSet, Machine, InfrastructureMachine, and all referenced KubeadmConfig, Secret, and ConfigMap objects.
Phase 2: Reconciliation Pausing (Split-Brain Prevention)
If controllers in both the source and target clusters reconciled the same cloud resources simultaneously, they would issue conflicting API calls (e.g., competing rolling updates or duplicate VM teardowns).
To prevent this, clusterctl sets:
spec:
paused: true
on all Cluster resources in the source cluster. Source controllers immediately halt reconciliation loops.
Phase 3: Object Transformation & Secret Preservation
clusterctl strips runtime metadata from the exported objects:
- Removes
metadata.resourceVersion,metadata.uid, andmetadata.selfLink. - Preserves all CA certificates, bootstrap tokens, and cloud credential
Secrets. - Deliberately strips the
.statusstanza.
Phase 4: Safe Source Object Purge
clusterctl deletes the custom resources from the source cluster. Crucially, CAPI controllers are programmed to respect the pause state: because spec.paused is true, deleting a Machine or Cluster from the source cluster does not invoke cloud API calls to delete the underlying VMs or load balancers.
Phase 5: Unpausing & Status Re-Derivation
Once the resources exist on the target cluster, clusterctl unpauses the Cluster (spec.paused = false). The target controllers adopt the resources, query the cloud provider using the recorded spec.providerID, and rebuild .status and condition trees from live infrastructure.
5. Why Status Is Not Transferred
A fundamental design principle of Kubernetes and Cluster API is:
Desired state (
spec) is declared; observed state (status) is derived from reality.
clusterctl move intentionally does not migrate .status blocks. The target management cluster’s controllers are expected to inspect the external cloud provider, query workload node APIs, evaluate certificate expiration dates, and rebuild the status condition tree from scratch.
Immediately after running clusterctl move, running kubectl get machines on the target cluster may show machines briefly in an uninitialized phase while informers populate and cloud provider queries resolve. Within one or two reconciliation loops, status converges cleanly.
6. Management Topologies: Self-Managing vs. Dedicated
Once pivoted, how should the management plane be organized?
flowchart TD
subgraph SelfManaging["Pattern A: Self-Managing Management Cluster"]
SM_CTRL["CAPI Controllers"]
SM_NODES["Management Nodes (AWS)"]
SM_CTRL -->|"Reconciles its own MachineDeployments"| SM_NODES
SM_CTRL -->|"Reconciles external Workload Clusters"| WC1["Workload Cluster 1"]
end
subgraph Dedicated["Pattern B: Dedicated Management Cluster (Hub-and-Spoke)"]
D_MGMT["Dedicated Management Cluster (EKS / Bare-Metal)"]
D_CTRL["CAPI Controllers"]
D_MGMT --- D_CTRL
D_CTRL -->|"Only manages external clusters"| WC_A["Workload Cluster A"]
D_CTRL -->|"Only manages external clusters"| WC_B["Workload Cluster B"]
D_CTRL -->|"Only manages external clusters"| WC_C["Workload Cluster C"]
end
Pattern A: Self-Managing Cluster
The management cluster’s own nodes are represented as CAPI Machine objects inside its own etcd. When the management cluster scales or upgrades itself, its local controllers provision and replace its own host VMs.
- Pros: Minimal resource footprint; zero external cluster dependencies.
- Cons: High blast radius; a corrupted controller upgrade can lock operators out of the management cluster.
Pattern B: Dedicated Management Cluster (Hub-and-Spoke)
The management cluster is hosted on a managed Kubernetes service (e.g., Amazon EKS, Azure AKS, Google GKE) or dedicated hardware. It never hosts business workloads and only manages external CAPI workload clusters.
- Pros: Complete failure isolation; workload cluster churn cannot starve management plane controllers.
- Cons: Requires running a dedicated cluster purely for management plane operations.
7. Diagnostic Workflows: Troubleshooting Pivot Failures
When clusterctl move fails or stalls, use a structured decision tree to diagnose the failure:
flowchart TD
START["clusterctl move Fails or Hangs"] --> CHECK{"Where Did Failure Occur?"}
CHECK -->|"Dry-Run / Preflight"| PRE{"Preflight Error"}
PRE -->|"Provider Missing"| P1["Target cluster lacks required CAPI providers.<br/>Run clusterctl init on target first"]
PRE -->|"Version Incompatible"| P2["Target provider versions < source provider versions.<br/>Upgrade providers on target"]
CHECK -->|"During Move Execution"| MOV{"Execution Error"}
MOV -->|"Blocked by Finalizers"| M1["Inspect source resources for custom non-CAPI finalizers<br/>preventing deletion"]
MOV -->|"Active Rollout Conflict"| M2["Cluster was mid-upgrade or mid-scale.<br/>Wait for cluster to reach steady-state"]
CHECK -->|"Post-Move on Target"| PST{"Target Reconciliation Stuck"}
PST -->|"Controllers Not Running"| T1["Check target worker capacity: pods stuck in Pending<br/>due to control-plane NoSchedule taints"]
PST -->|"Cloud Credentials Missing"| T2["Ensure cloud credential Secrets (e.g. AWS credentials)<br/>were copied to target namespace"]
Essential Diagnostic Commands
1. Pre-Move Verification (Always Dry-Run First)
clusterctl move \
--to-kubeconfig target.kubeconfig \
--dry-run \
-v 5
Inspect the output to verify:
- All
Clusters,Machines, andMachineDeploymentsare discovered. - No dangling external resources block migration.
2. Verify Provider Parity
# Check providers on source
kubectl get providers -A
# Check providers on target
kubectl --kubeconfig target.kubeconfig get providers -A
Target provider versions must be greater than or equal to source provider versions.
3. Inspect Stalled Move Events
If clusterctl move hangs while deleting from source, inspect source resources for blocking finalizers:
kubectl get clusters,machines -o custom-columns=NAME:.metadata.name,FINALIZERS:.metadata.finalizers
8. Key Takeaways
- The Chicken-and-Egg Solution: Ephemeral bootstrap clusters (KinD) exist solely to break the paradox of declarative cluster creation. They instantiate the real management cluster and are discarded after the pivot.
- Infrastructure Does Not Move:
clusterctl movetransfers CRD objects, etcd state, and reconciliation authority. The underlying virtual machines and load balancers continue running without interruption. - Split-Brain Prevention:
clusterctl movepauses source clusters (spec.paused = true) before exporting objects, ensuring two controller planes never compete to manage the same cloud resources. - Status Is Re-Derived:
.statusis never migrated. Target controllers adopt resources and derive health conditions from the actual cloud infrastructure. clusterctl moveIs Not Disaster Recovery:moveis an interactive migration protocol designed for the pivot workflow. True disaster recovery requires etcd snapshots and backup tools like Velero.
Next in the series
Now that we understand how management clusters are bootstrapped and pivoted, explore Part 14: Running Cluster API in Production to examine multi-tenant management architectures, disaster recovery strategies, controller high-availability, and GitOps workflows at scale.