Setting up Talos Linux + Kubernetes + Cilium on Bare Metal
by Afanasy Barbarov
Setting up Talos Linux + Kubernetes + Cilium on Bare Metal
Real-time documentation of setting up a 3-node Kubernetes cluster using Talos Linux and Cilium networking on bare metal VPS servers.
The stack
- Talos Linux - immutable, API-driven OS built for Kubernetes (no SSH)
- Kubernetes - vanilla upstream k8s
- Cilium - eBPF-based networking, replaces kube-proxy, handles ingress, mTLS, network policies
Hardware
3× VPS servers:
- 4 dedicated cores
- 8 GB DDR5 ECC RAM
- 256 GB NVMe
- IPv4 + IPv6
Step 1: Install talosctl
On macOS:
brew install siderolabs/tap/talosctlVerify:
> talosctl version --client
Client:
Tag: v1.12.2
SHA: 54e5b438
Built:
Go version: go1.25.6
OS/Arch: darwin/arm64Step 2: Generate cluster config
Generate the config with your cluster name and control plane endpoint:
cd ~/projects/infrastructure
talosctl gen config echo https://<control-plane-ip>:6443This creates:
controlplane.yaml- config for control plane nodesworker.yaml- config for worker nodes (not needed, all nodes are control plane)talosconfig- client credentials (mTLS certificates)
STOP. Back up these files NOW. Save all three to your password manager (macOS Keychain, 1Password, whatever). The controlplane.yaml contains cluster secrets. But talosconfig is the one you'll cry about if lost - it's your only key into the cluster. Lose it = lose cluster forever. No recovery.
Fix the install disk path. By default, Talos config assumes /dev/sda. Your VPS might use /dev/vda. If you skip this, Talos won't install to disk and you'll boot back into whatever was there before.
sed -i '' 's|/dev/sda|/dev/vda|g' controlplane.yaml
grep "disk:" controlplane.yaml # verify it shows /dev/vdaStep 3: Boot Talos on servers
Download Talos ISO:
cd ~/projects/infrastructure
curl -LO https://github.com/siderolabs/talos/releases/download/v1.12.2/metal-amd64.isoIn your VPS provider's control panel for each server:
- Upload the ISO and mount it as a DVD/virtual media
- Set boot mode to DVD
- Power off and power on
Server boots into Talos maintenance mode. Verify it's up:
talosctl get disks --insecure --nodes <node-ip>You should see your disk (probably vda):
NODE NAMESPACE TYPE ID VERSION SIZE READ ONLY TRANSPORT ROTATIONAL
runtime Disk vda 2 275 GB false virtio trueStep 4: Apply config to nodes
For each node, apply config and verify. No output = success. Talos installs to disk and reboots.
Node 1 (control plane endpoint):
talosctl apply-config --insecure --nodes <node1-ip> --file controlplane.yaml
# wait 1-2 min for reboot
talosctl get members --insecure -n <node1-ip> -e <node1-ip>Node 2:
talosctl get disks --insecure --nodes <node2-ip>
talosctl apply-config --insecure --nodes <node2-ip> --file controlplane.yaml
# wait for reboot
talosctl get members --insecure -n <node2-ip> -e <node2-ip>Node 3:
talosctl get disks --insecure --nodes <node3-ip>
talosctl apply-config --insecure --nodes <node3-ip> --file controlplane.yaml
# wait for reboot
talosctl get members --insecure -n <node3-ip> -e <node3-ip>Empty members table = node is up, waiting for bootstrap.
Important: After applying config, go back to the control panel and eject the ISO from each server, then restart. Otherwise the server keeps booting from DVD (maintenance mode) instead of disk (installed Talos). You'll get certificate errors if you skip this.
After ejecting and restarting, verify node is running from disk:
talosctl version --talosconfig ./talosconfig --nodes <node-ip> --endpoints <node-ip>You should see both Client and Server versions (v1.12.2). If you see "certificate signed by unknown authority" - the ISO is still attached. If you see the old OS login in the console - the disk path was wrong (/dev/sda instead of /dev/vda).
Step 5: Bootstrap cluster
Bootstrap initializes etcd. Run once on one node only:
talosctl bootstrap --talosconfig ./talosconfigCheck cluster is forming:
talosctl get members --talosconfig ./talosconfigAll 3 nodes should appear as controlplane.
Run health check:
talosctl health --talosconfig ./talosconfigWait until all checks pass (etcd, kubelet, control plane components, coredns).
Step 6: Get kubeconfig
talosctl kubeconfig --talosconfig ./talosconfigThis writes to ~/.kube/config. Verify:
kubectl get nodesOutput:
NAME STATUS ROLES AGE VERSION
node-1 Ready control-plane 65s v1.35.0
node-2 Ready control-plane 83s v1.35.0
node-3 Ready control-plane 83s v1.35.0Step 7: Install Cilium
Install Cilium CLI:
brew install cilium-cliImportant: Don't run plain cilium install - it will fail on Talos with "unable to apply caps: operation not permitted" errors. Talos blocks certain Linux capabilities for security.
Install Cilium with Talos-compatible settings:
cilium install \
--set kubeProxyReplacement=true \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445 \
--set bpf.hostLegacyRouting=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroupKey settings:
kubeProxyReplacement=true- Cilium replaces kube-proxyk8sServiceHost=localhost:7445- uses Talos KubePrism endpoint- Removed
SYS_MODULEfrom capabilities (Talos blocks it) bpf.hostLegacyRouting=true- required for DNS to work with Talos
Verify:
cilium status