Cluster API Pivot
How the management cluster is bootstrapped using a local kind cluster, then pivoted to run on OpenStack VMs via Cluster API.
Overview
The management cluster (clusters/mgmt/) is the CAPI management cluster. It cannot manage itself from nothing — it needs an initial cluster to run the CAPI controllers. The standard approach is to bootstrap a temporary kind cluster, install CAPI providers on it, create the mgmt cluster on OpenStack VMs, then pivot (move) all CAPI resources to the target.
After the pivot, the mgmt cluster becomes self-managing: it runs the same CAPI providers that created it, reconciled by Flux from the Argus repository.
::: note Only the mgmt cluster is managed by CAPI. The OpenStack cluster (clusters/openstack/) runs on baremetal nodes bootstrapped with kubeadm and is not involved in the CAPI pivot. See OpenStack Cluster Bootstrap for the baremetal path. :::
┌──────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
│ Local kind │ │ OpenStack VMs │ │ Self-managing │
│ cluster │────>│ (CAPO-provisioned) │────>│ mgmt cluster │
│ │ │ │ │ │
│ clusterctl init │ │ clusterctl move │ │ Flux reconciles │
│ creates mgmt │ │ pivots providers │ │ CAPI providers │
│ cluster here │ │ to target │ │ from Git │
└──────────────────┘ └──────────────────────┘ └─────────────────────┘
Phase 1 Phase 2 Phase 3Prerequisites
- Docker (for running kind)
clusterctlCLI (v1.12.x)kubectlconfigured for the target OpenStack cluster- Access to the OpenStack cluster with admin credentials
- The
capo-variablessecret must be created on the target cluster first
Phase 1: Bootstrap with Kind
1. Create a kind cluster
kind create cluster --name capi-mgmt --wait 5mThis creates a local Kubernetes cluster in Docker that will serve as the temporary management plane.
2. Initialize Cluster API
clusterctl init --infrastructure openstackThis installs:
- CAPI core provider (v1.13.2)
- Kubeadm bootstrap provider (v1.13.2)
- Kubeadm control plane provider (v1.13.2)
- OpenStack infrastructure provider / CAPO (v0.14.4)
The provider versions are pinned to match the declarative CRs in infrastructure/cluster-api-providers/.
3. Create the OpenStack credentials secret
CAPO needs OpenStack credentials to provision VMs:
kubectl --context kind-capi-mgmt -n capo-system create secret generic capo-variables \
--from-file=clouds.yaml=<path-to-clouds.yaml>4. Create the ClusterClass and templates
Apply the reusable templates from the Argus repository:
kubectl --context kind-capi-mgmt apply -f infrastructure/cluster-api-templates/This creates:
- ClusterClass
openstack-default - KubeadmControlPlaneTemplate
openstack-default-control-plane-v1 - KubeadmConfigTemplate
openstack-default-worker-v1 - OpenStackClusterTemplate
openstack-default-cluster-v1 - OpenStackMachineTemplate
openstack-default-{control-plane,worker}-v1
5. Create the target mgmt cluster
Apply the Cluster CR that references the ClusterClass:
kubectl --context kind-capi-mgmt apply -f clusters/mgmt/clusters/mgmt.yamlCAPO provisions OpenStack VMs, kubeadm bootstraps Kubernetes, and the mgmt cluster comes up with a working control plane.
6. Wait for the target cluster to be ready
clusterctl get kubeconfig mgmt -n mgmt > /tmp/mgmt.kubeconfig
kubectl --kubeconfig /tmp/mgmt.kubeconfig get nodesWait until all control-plane nodes are Ready.
Phase 2: Pivot (clusterctl move)
1. Install CAPI providers on the target cluster
Before moving, the target cluster needs the CAPI operator and providers installed. These will be managed by Flux after the pivot, but they need to exist for clusterctl move to work:
# Install cert-manager (CAPI operator dependency)
kubectl --kubeconfig /tmp/mgmt.kubeconfig apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.2/cert-manager.yaml
# Install CAPI operator
helm --kubeconfig /tmp/mgmt.kubeconfig upgrade --install capi-operator \
kubernetes-sigs.github.io/cluster-api-operator \
-n capi-operator-system --create-namespace \
--version 0.27.0 --wait2. Apply provider CRs
kubectl --kubeconfig /tmp/mgmt.kubeconfig apply -f infrastructure/cluster-api-providers/This creates the CAPI provider deployments on the target cluster. The clusterctl-providers.yaml file provides the v1alpha3 Provider inventory CRs that clusterctl move needs to discover providers on the target.
WARNING
The clusterctl-providers.yaml file exists solely to make clusterctl move work. The Cluster API Operator does not create these v1alpha3 CRs — without them, clusterctl move fails with "provider not found."
3. Move resources
clusterctl move --to-kubeconfig /tmp/mgmt.kubeconfig -n mgmtThis transfers:
- Cluster resources (Cluster, MachineDeployment, etc.)
- ClusterClass and templates
- Infrastructure templates
- Bootstrap templates
::: note clusterctl move transfers CAPI resources (clusters, machines, templates) but NOT the provider deployments themselves. The providers on the target cluster are already running from step 1-2. :::
4. Verify the move
kubectl --kubeconfig /tmp/mgmt.kubeconfig get cluster -A
kubectl --kubeconfig /tmp/mgmt.kubeconfig get kubeadmcontrolplane -A
kubectl --kubeconfig /tmp/mgmt.kubeconfig get openstackcluster -AAll resources should be present on the target cluster.
Phase 3: Self-Management via GitOps
1. Clean up the kind cluster
Once the pivot is verified, delete the temporary kind cluster:
kind delete cluster --name capi-mgmt2. Install Cilium and Flux
Install Cilium (CNI) and Flux (GitOps) on the mgmt cluster so it can reconcile itself from Git. See Management Cluster — Flux Deployment for the full component stack and dependency order.
# Install Cilium
helm upgrade --install cilium cilium/cilium -n kube-system \
--kubeconfig /tmp/mgmt.kubeconfig \
-f ./infrastructure/cilium/values.yaml --version 1.18.6
# Install Flux Operator
kustomize build infrastructure/fluxcd/operator/ | \
kubectl --kubeconfig /tmp/mgmt.kubeconfig apply -f -
# Apply Flux Instance (syncs from ./clusters/mgmt)
kustomize build clusters/mgmt/fluxcd/ | \
kubectl --kubeconfig /tmp/mgmt.kubeconfig apply -f -3. Create the capo-variables secret
The mgmt cluster needs the OpenStack credentials secret (not managed by Flux — placed manually):
kubectl --kubeconfig /tmp/mgmt.kubeconfig -n capo-system create secret generic capo-variables \
--from-file=clouds.yaml=<path-to-clouds.yaml>See Cluster API Providers README for details.
4. Verify Flux reconciliation
kubectl --kubeconfig /tmp/mgmt.kubeconfig get kustomization -n flux-system
kubectl --kubeconfig /tmp/mgmt.kubeconfig get helmrelease -AAll Kustomizations should eventually reach Ready status. The CAPI providers, ClusterClass, and templates are now reconciled from Git.
Summary
| Phase | What runs where | Purpose |
|---|---|---|
| Phase 1 | kind cluster (local Docker) | Temporary bootstrap plane |
| Phase 2 | Target OpenStack VMs | Receive pivoted CAPI resources |
| Phase 3 | Target OpenStack VMs + Flux | Self-managing via GitOps |
After Phase 3, the mgmt cluster:
- Runs CAPI providers that manage itself
- Reconciles all configuration from the Argus repository
- Can provision new OpenStack-backed clusters via the ClusterClass
- No manual
clusterctloperations needed for day-to-day management
Troubleshooting
clusterctl move fails with "provider not found"
Ensure clusterctl-providers.yaml is applied on the target cluster. This file provides the v1alpha3 Provider inventory CRs that clusterctl move uses to discover providers.
CAPI providers crash-loop on mgmt
Check that ORC (OpenStack Resource Controller) is installed. CAPO v0.14.x depends on ORC for image resolution. ORC is installed as a standalone Flux Kustomization (clusters/mgmt/orc.yaml), not as a CAPI provider.
Pod networking fails after pivot
The ClusterClass templates include explicit Cilium security group rules (VXLAN UDP 8472, health TCP 4240, Hubble TCP 4244). Without these, CAPO's default managed security groups drop Cilium overlay traffic. Verify with:
openstack security group rule list k8s-cluster-mgmt-mgmt-secgroup-controlplane