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/talosctl

Verify:

> talosctl version --client
Client:
	Tag:         v1.12.2
	SHA:         54e5b438
	Built:
	Go version:  go1.25.6
	OS/Arch:     darwin/arm64

Step 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>:6443

This creates:

  • controlplane.yaml - config for control plane nodes
  • worker.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/vda

Step 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.iso

In your VPS provider's control panel for each server:

  1. Upload the ISO and mount it as a DVD/virtual media
  2. Set boot mode to DVD
  3. 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      true

Step 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 ./talosconfig

Check cluster is forming:

talosctl get members --talosconfig ./talosconfig

All 3 nodes should appear as controlplane.

Run health check:

talosctl health --talosconfig ./talosconfig

Wait until all checks pass (etcd, kubelet, control plane components, coredns).

Step 6: Get kubeconfig

talosctl kubeconfig --talosconfig ./talosconfig

This writes to ~/.kube/config. Verify:

kubectl get nodes

Output:

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.0

Step 7: Install Cilium

Install Cilium CLI:

brew install cilium-cli

Important: 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/cgroup

Key settings:

  • kubeProxyReplacement=true - Cilium replaces kube-proxy
  • k8sServiceHost=localhost:7445 - uses Talos KubePrism endpoint
  • Removed SYS_MODULE from capabilities (Talos blocks it)
  • bpf.hostLegacyRouting=true - required for DNS to work with Talos

Verify:

cilium status

Written by Afanasy Barbarov — Tech Lead with 15+ years shipping production systems in Rust, Go, and TypeScript. Facing a similar challenge? Reach out on LinkedIn. Support my work.

More articles

Previous post

SigNoz in a Nomad (+ Consul) cluster.

Read more

Next post

Cilium Network Policies.

Read more