After provisioning VMs with Terraform, the next step is turning them into a Kubernetes cluster. I use Talos Linux. It’s a minimal OS designed specifically for Kubernetes. No SSH, no package manager, no random services running. You manage it through the API using talosctl. That design keeps the OS simple and predictable.
I like this because there’s no config drift. You can’t accidentally break things by installing a package or editing a config file. The nodes stay in the state you defined them in.
Why I chose Talos#
When I first set up Kubernetes in my homelab, I used Ubuntu with kubeadm. It worked but it felt fragile. Updates would sometimes break things. I’d accidentally change something on one node that I forgot to change on the others. The OS felt like a distraction from actually running Kubernetes.
Talos solved that. The OS gets out of the way. You define your cluster in code, apply it, and the nodes stay exactly like that. If something goes wrong, you reapply the config and it fixes itself.
Prerequisites#
You need:
- Talos VMs from Part 1
talosctlCLI installedkubectlinstalled- Static IPs for your nodes (or DHCP with reservations so the IPs don’t change)
Step 1: Generate the cluster config#
talosctl gen config totorolab https://controlplane-ip:6443 \
--output-dir ./outThis creates three files:
controlplane.yamlfor control plane nodesworker.yamlfor worker nodestalosconfigfor connecting to your cluster via talosctl
Customize them if you need to. Usually defaults are fine.
Step 2: Apply the config to each node#
For each control plane node:
talosctl apply-config --insecure \
--nodes <control-plane-ip> \
--file ./out/controlplane.yamlSame for workers with worker.yaml. The --insecure flag is temporary. You need it on first boot because the cluster doesn’t exist yet to verify certificates.
Do this for all your nodes. They’ll reboot with Talos running.
Step 3: Bootstrap the cluster#
Once all nodes have their config applied:
talosctl bootstrap --nodes <any-control-plane-ip>
talosctl kubeconfig --nodes <any-control-plane-ip>Wait a minute or two for the cluster to elect a leader and come up. Then check:
kubectl get nodesYou should see all your nodes in Ready state.
Where I went wrong#
The first time I did this, I applied the worker config to a control plane node by mistake. It took me longer than I’d like to admit to figure out why the cluster wouldn’t bootstrap. The lesson is double-check the node list before running commands.
Also, I initially didn’t set static IPs. Nodes would get DHCP leases that expired and get new IPs, breaking everything. Make sure your DHCP is either disabled or you have persistent reservations.
Next up#
Your cluster is running but it’s bare bones. No networking, no ingress, no storage. Part 3 covers Flux CD, which manages all the cluster infrastructure automatically using Git.