clusterctl provisions and manages Talos Linux
Kubernetes clusters on OpenStack from a single declarative file. You describe
the cluster you want in cluster.yaml; clusterctl converge makes reality
match it — create, scale up, scale down (drain first), and rolling
Talos/Kubernetes upgrades are all the same command. Re-running is always safe.
There is no state file. Every resource is named deterministically and tagged
(managed-by=clusterctl, cluster=<name>); converge discovers what exists by
tag, creates what's missing, and never touches resources it didn't create.
Requires talosctl and kubectl on PATH. With
uv nothing else is needed:
uv tool install git+https://github.com/ncsa/clusterctl # install
uv tool upgrade clusterctl # updateOr run it without installing:
uvx --from git+https://github.com/ncsa/clusterctl clusterctl --helpFrom a checkout of this repo: uv run clusterctl --help, or with pip:
python3 -m venv .venv && . .venv/bin/activate && pip install -e .
clusterctl init mycluster # scaffold cluster.yaml, secrets.yaml, .gitignore
vi secrets.yaml # openstack application credential + tailscale key
vi cluster.yaml # versions, pools, openstack endpoint, allowlists
clusterctl plan # dry-run: print every action, change nothing
clusterctl converge # build the clusterThe machine running clusterctl must be on the tailnet — the initial bootstrap reaches the first controlplane node by its tailscale name.
| command | what it does |
|---|---|
init [NAME] |
scaffold cluster.yaml / secrets.yaml / .gitignore; never overwrites existing files |
plan |
dry-run converge: print every create/update/delete |
converge |
make the cluster match cluster.yaml (phases: image → secrets → network/SG → discover → scale-down → upgrade → compute → bootstrap → kubeconfig → health) |
status |
list managed OpenStack resources + kubectl get nodes |
dashboard [NODE...] |
talosctl dashboard on all (reachable) nodes, or just the ones given |
env |
print export OS_* lines for the openstack CLI: eval "$(clusterctl env)" |
image download|remove |
build/upload the Glance boot image, or delete it (converge never deletes it) |
destroy |
delete every managed resource + local state; the shared boot image is kept |
Options: every command takes -C DIR to operate on another cluster directory.
converge, image and destroy take --dry-run (print, don't do) and
--yes (skip the confirmation that any deletion otherwise requires).
Typical cluster.yaml edits and what converge does with them: bump a pool
count to add nodes; lower it to drain + remove them; bump talos.version
or kubernetes.version for a rolling upgrade (existing nodes are upgraded
before new ones are added); edit a security allowlist to reconcile the
security-group rules.
cluster.yaml— desired state: versions, node pools, network, allowlists, extensions. Committable, but note thesecurityallowlists reveal which source addresses may reach your APIs.secrets.yaml— OpenStack application credential + tailscale pre-auth key. Gitignored; restorable by reissuing credentials.talossecrets.yaml—⚠️ the cluster's cryptographic identity (cluster CA, etcd CA, join tokens). Generated on the first converge, gitignored, mode 0600. It cannot be regenerated for a running cluster — back it up out-of-band like a private CA key.destroydeletes it on purpose, so the next converge starts a brand-new cluster.
(talosconfig and kubeconfig are derived from these and regenerated by
converge; safe to delete.)
cluster.yaml pins both versions; nothing auto-upgrades. Find targets with:
curl -s https://api.github.com/repos/siderolabs/talos/releases/latest | jq -r .tag_name
talosctl gen config --help | grep kubernetes-version # k8s pairing Talos testedTwo rules: upgrade Kubernetes one minor at a time, and bump Talos before
Kubernetes when moving both (converge already orders it that way within a
run). Bumping talos.version builds a new ~1 GB boot image via
factory.talos.dev on the next converge.
Every node boots from one shared image per Talos version
(talos-<version>-tailscale, baked with tailscale + qemu-guest-agent).
Tailscale only authenticates if a key is present in secrets.yaml. Extra
extensions (e.g. nvidia for a GPU pool) and freeform machine-config patches go
in cluster.yaml, cluster-wide under talos: or per worker pool:
workers:
gpu:
count: 2
flavor: gpu.a100
disk: 100
extensions:
- siderolabs/nonfree-kmod-nvidia
- siderolabs/nvidia-container-toolkit
config_patches:
- |
machine:
sysctls: {...}Extra extensions take effect on the node's first upgrade pass (on OpenStack
the boot image is the first-boot system; the factory installer image applies
on talosctl upgrade), which converge handles automatically.