Install the provider¶
Installing CAPVCO means teaching clusterctl where to find it, then running clusterctl init
against your management cluster. This installs Cluster
API’s core + kubeadm providers and CAPVCO’s CRDs and controller.
You do this once per management cluster.
1. Register the provider with clusterctl¶
CAPVCO is hosted on GitLab. clusterctl consumes a GitLab provider from the project's Package
Registry (not the human release page), over the gitlab.whitesky.cloud host — so add a
providers: entry pointing at the release's infrastructure-components.yaml in the registry to
~/.cluster-api/clusterctl.yaml (create the file if it doesn't exist):
providers:
- name: vco
type: InfrastructureProvider
url: "https://gitlab.whitesky.cloud/api/v4/projects/730/packages/generic/capvco/v0.6.1/infrastructure-components.yaml"
v0.6.1 is the latest published release (filled in automatically when
these docs are built). To pin a specific version, put its tag in that path segment —
e.g. …/packages/generic/capvco/vX.Y.Z/infrastructure-components.yaml. metadata.yaml and the
cluster templates live in the same package, so clusterctl fetches everything it needs from there.
Use a clusterctl that speaks v1beta2
CAPVCO targets the v1beta2 Cluster API contract, so you need clusterctl v1.14.x (matching the CAPI the provider is built on). An older one fails with "current version of clusterctl is only compatible with v1beta1 providers, detected v1beta2", and anything before v1.13.6 cannot bootstrap Kubernetes v1.37 at all — which CAPVCO ships node images for. See Prerequisites → tools to upgrade.
2. Provide the instance URLs¶
The manager reads the two instance URLs from its
environment, sourced from a Kubernetes Secret named capvco-credentials in its namespace
(capvco-system). Create that Secret before the manager starts:
kubectl create namespace capvco-system
kubectl -n capvco-system create secret generic capvco-credentials \
--from-literal=portalUrl="https://<your-whitesky.cloud-portal>" \
--from-literal=iamRefreshUrl="https://<your-iam-host>/v1/oauth/jwt/refresh"
Use your portal's actual URLs — they're arbitrary per instance (there's no fixed console./iam.
prefix, and the IAM host is not derived from the portal host). A template is in
config/manager/credentials-secret.example.yaml.
This Secret is instance config, not a credential
It carries only the two URLs. The manager holds no standing whitesky.cloud credential — every backend operation uses the per-cluster credential (JWT + customer id) from each cluster's own Secret (see the first-cluster step), never a manager-wide one.
Order
clusterctl init (next step) creates the capvco-system namespace and the manager itself. If
you init first, the manager pod stays pending/crash-looping until this Secret exists — creating
it up front (as above) avoids that.
3. Initialise¶
This installs, into your management cluster:
- Cluster API core, bootstrap (kubeadm) and control-plane (kubeadm) controllers
- CAPVCO’s CRDs (
vcoclusters,vcomachines, and the two*templates) - the CAPVCO manager in the
capvco-systemnamespace
Expected tail of the output:
Your management cluster has been initialized successfully!
You can now create your first workload cluster by running the following:
clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -
4. Install the CNI add-on¶
Cluster API does not install a pod network for you — and without one, every workload node stays
NotReady forever (cni plugin not initialized). CAPVCO ships the CNI as a ClusterResourceSet:
apply it once to the management cluster and every cluster you create afterwards installs Calico
automatically. The templates label each Cluster with cni: calico, and this ClusterResourceSet
targets that label, so there's nothing to run per-cluster.
kubectl apply -f https://gitlab.whitesky.cloud/api/v4/projects/730/packages/generic/capvco/v0.6.1/cni.yaml
This creates the capvco-cni-calico ClusterResourceSet and its Calico ConfigMap in the default
namespace — where your workload Cluster objects live. If you create clusters in another namespace,
apply it there too with -n <namespace>.
The same CNI bundle covers geo
There's a single CNI bundle. Its Calico is tuned to the cross-site tunnel MTU (veth_mtu: 1350),
so geo-redundant clusters need nothing extra — and any cluster can later expand into more
locations without a CNI change. See Geo-redundant clusters.
If you changed POD_CIDR, the CNI pool must match
The bundled Calico is pinned to the templates' default POD_CIDR (10.244.0.0/16). If you
override POD_CIDR when generating a cluster, the CNI pool must match — otherwise pods get
addresses from the wrong range. Most users never change it.
Storage (CSI) is a separate add-on
The base template also labels clusters csi: vco. Persistent volumes need the CSI add-on, which
is published in the same release as csi.yaml — but its driver image lives on your instance's
(white-label) registry, so it needs one substitution at apply time. Set it up when you need
PVCs — see Persistent storage. A cluster reaches Ready without it.
5. Verify¶
kubectl get providers -A # installed providers + versions (infrastructure-vco)
kubectl -n capvco-system get pods # the manager pod should be Running/Ready
kubectl get crds | grep vco # vcoclusters / vcomachines / *templates present
kubectl get clusterresourceset # capvco-cni-calico present (the CNI add-on from step 4)
You should see infrastructure-vco in the providers list, the manager pod 1/1 Running, the four
CRDs, and the capvco-cni-calico ClusterResourceSet. (There is no clusterctl describe provider
command; for a fuller view of installed providers, versions and the contract, use
clusterctl upgrade plan.)
ClusterResourceSet is enabled by default
The CNI add-on relies on Cluster API's ClusterResourceSet controller, which is on by default in
the clusterctl version these docs target (v1.14.x) — no feature gate to enable. If
kubectl get clusterresourceset errors with the server doesn't have a resource type, you're on
a build with it disabled; re-run clusterctl init with EXP_CLUSTER_RESOURCE_SET=true exported.
Developing CAPVCO itself?
Instead of installing the released image, you can run the manager out-of-cluster against your kind management cluster — see Contributing. Everything else in these docs is identical.
Upgrading later¶
Bump the provider url version in clusterctl.yaml, then:
Air-gapped installs¶
If the management cluster has no internet egress, mirror the CAPVCO manager image and
infrastructure-components.yaml (plus the core/bootstrap/control-plane images) into a private
registry and point clusterctl at the mirrored components. For workload clusters with no
egress, see the air-gapped guide.
Installing with GitOps instead of clusterctl¶
CAPVCO installs perfectly well from infrastructure-components.yaml through Flux,
Argo CD, Sylva or plain Kustomize. Two things catch people out when they do, both
reported from a real Sylva integration.
clusterctl move refuses to run against a GitOps-installed provider
clusterctl init registers Provider inventory records in the management
cluster. Installing the same manifests through Flux or Argo does not — the
inventory is a clusterctl-only concept. clusterctl move checks it and stops:
failed to check providers in target cluster:
provider infrastructure-vco not found ... provider cluster-api not found
The controllers are running correctly; only the bookkeeping is absent. To pivot
into a GitOps-installed cluster, register matching Provider records on the
target first — one per provider, with the same version as the source
inventory, in the namespace where each controller actually runs (CAPVCO's may
not be capvco-system if your GitOps layer chose another).
Verify each deployed image tag matches the version you record. Registering an inventory that disagrees with what is deployed turns a clear refusal into a silent mismatch.
Overriding the manager image needs the post-transformation name
infrastructure-components.yaml has already had its Kustomize image
transformations applied, so the container is no longer named controller — it
is the full ghub.gig.tech/whitesky-oss/cluster-api-provider-vco. A Kustomize
images: override written against controller matches nothing, silently
leaves whatever tag was there, and surfaces only as:
Match the full name and set newTag to the release you want.
Provider up and healthy? → Your first cluster.