Your first cluster¶
With the provider installed, you’ll now create a small but real cluster: one control-plane node and one worker, in a single whitesky.cloud location. We’ll go slowly and show the output to expect at each step.
What you’ll do
- Create the credential Secret · 2. Render a manifest · 3. Apply & watch · 4. (next page) get the kubeconfig and deploy an app.
1. Create the credential Secret¶
Each VCOCluster points at a Kubernetes Secret holding the whitesky.cloud credential used to provision and
run the cluster. Create it in the management cluster, in the namespace your cluster will live in
(default here):
kubectl create secret generic my-cluster-vco-credential \
--from-literal=jwt="<your self-refreshing whitesky.cloud JWT>" \
--from-literal=customer_id="<your customer id>"
Keys:
jwt,customer_id— required.portal_url,iam_refresh_url— optional per-cluster overrides of the manager’s instance defaults (only needed if this cluster targets a different whitesky.cloud portal than the manager’s default).
Treat the JWT as a live secret
It provisions and controls real infrastructure. Don’t commit it; rotate it when no longer needed. CAPVCO reads it from this Secret — it never logs or copies it elsewhere except the controlled injection into the workload cluster for the CSI driver.
The Secret name here (my-cluster-vco-credential) follows the template default
${CLUSTER_NAME}-vco-credential. Keep that convention and the template finds it automatically.
2. Render a cluster manifest¶
You don’t hand-write the objects — you render a template with
clusterctl generate cluster. Start with the base template:
VCO_LOCATION=<your-location> \
VCO_IMAGE_ID=<stock-ubuntu-image-id-in-that-location> \
VCO_EXTNET_ID=<external-network-id-in-that-location> \
clusterctl generate cluster my-cluster \
--kubernetes-version v1.35.8 \
--infrastructure vco \
--worker-machine-count 1 > my-cluster.yaml
Three things that silently bite if you skip them
VCO_IMAGE_IDis required — there is no default. vm-image ids are location-specific (an id from one location doesn't exist in another), so no portable default is possible. Find the stock-Ubuntu image id for your location in the whitesky.cloud portal (or the VCO API); see pre-baked images for the same lookup. Omit it andclusterctlerrors withvalue for variables [VCO_IMAGE_ID] is not set— which is the point: it fails loudly instead of booting the wrong image.VCO_EXTNET_IDis required — there is no default. It selects the external network your cluster's cloudspace attaches to (the source of its public IP / egress). If your customer has more than one external network, letting the backend pick its default silently attaches the cluster to the wrong one — so you must choose. List your external networks in the portal (or the VCO API) and pass the id for the network this cluster should use. Omit it andclusterctlerrors withvalue for variables [VCO_EXTNET_ID] is not set. (Geo clusters set this per-location viaspec.geo.locationExternalNetworksinstead — see geo-redundancy.)--worker-machine-countdefaults to0. Leave it off and you get a control plane with zero workers (nothing fails — you just silently get no worker node). Always pass--worker-machine-count 1(or more). This is aclusterctlflag; it overrides the template, so settingWORKER_MACHINE_COUNTas an env var has no effect.
Open my-cluster.yaml and skim it — you’ll recognise the objects from
Core concepts: a Cluster, a VCOCluster, a
KubeadmControlPlane + its VCOMachineTemplate, and a MachineDeployment + its template and a
MachineHealthCheck.
Common knobs
Override any of these on the command line:
| Variable | Default | Meaning |
|---|---|---|
CONTROL_PLANE_MACHINE_COUNT |
1 |
Control-plane replicas (use 3 for HA) |
VCO_CP_VCPUS / VCO_CP_MEMORY_MIB |
2 / 4096 |
Control-plane VM size |
VCO_MD_VCPUS / VCO_MD_MEMORY_MIB |
2 / 4096 |
Worker VM size |
POD_CIDR |
10.244.0.0/16 |
Pod network — must not overlap the cloudspace subnet |
(VCO_IMAGE_ID and the worker count aren't in this table on purpose — see the warning above:
the image id is required, and the worker count is the --worker-machine-count flag.)
The full list and every other template is in the configuration reference.
Base vs pre-baked image
The base template uses a stock Ubuntu image and installs containerd + Kubernetes on first
boot (simple, but slower and needs egress). For fast, deterministic, offline-friendly boots, use
a pre-baked image and the cluster-template-prebaked.yaml template. Start with
the base template here; switch later.
3. Apply and watch it build¶
cluster.cluster.x-k8s.io/my-cluster created
vcocluster.infrastructure.cluster.x-k8s.io/my-cluster created
kubeadmcontrolplane.controlplane.cluster.x-k8s.io/my-cluster-control-plane created
vcomachinetemplate.infrastructure.cluster.x-k8s.io/my-cluster-control-plane created
machinedeployment.cluster.x-k8s.io/my-cluster-md-0 created
...
Watch it converge — CAPVCO reports real whitesky.cloud state as conditions:
You’ll see the VCOCluster move through CloudspaceReady → EndpointReady → Ready as it creates
the cloudspace, publishes the control-plane load balancer, and the first control-plane node boots
and initialises.
Track the machines directly:
NAME CLUSTER PHASE AGE VERSION
my-cluster-control-plane-abcde my-cluster Provisioning 30s v1.35.8
my-cluster-md-0-xxxxx-yyyyy my-cluster Pending 30s v1.35.8
Machines go Pending → Provisioning → Provisioned → Running. A first cluster on a stock image
typically reaches a running control plane in a few minutes; the worker joins shortly after.
Stuck in Provisioning for a while?
On the stock image the node installs containerd + Kubernetes on first boot, which takes a
few minutes and needs egress to pkgs.k8s.io. If it never progresses, check the
troubleshooting guide — the usual causes are a bad credential, an
unreachable location, or a POD_CIDR that overlaps the cloudspace network.
Nodes are NotReady until the CNI lands — and that's automatic
A brand-new node has no pod network, so for a short while it registers NotReady with
container runtime network not ready: cni plugin not initialized. This is expected — not a
failed deploy. You don't fix it by hand: because you applied the CNI ClusterResourceSet
when you installed the provider, the management
cluster pushes Calico into the workload cluster automatically as soon as its API server is
reachable, and the node flips to Ready within a minute or so.
If nodes are still NotReady after several minutes, the CNI add-on almost certainly wasn't
applied to the management cluster — go back to that step. (You'll confirm Ready nodes on the
next page.)
When the control-plane machine is Running with a NODE NAME, the API server is up. Time to
access it and deploy something.
Next → Access it & deploy an app.