Build a Bare‑Metal Kubernetes Cluster on Hetzner with Talos Linux
Run Kubernetes on Hetzner VPS with Talos Linux 2, set up a secure multi‑node cluster, add Rook Ceph storage, and deploy a persistent NGINX app.
5 min read
Running Kubernetes on Hetzner’s VPS instances gives you a production-grade cluster without the complexity of a full data center. In this guide, we’ll walk through building a “bare-metal-style” cluster with Talos Linux 2 and configuring storage pools on the disks attached to each VPS. The steps are written for Hetzner Cloud VPS instances, but the concepts apply to any similar provider.
1. Why Talos Linux?
Immutable OS – Talos ships as a single, immutable image that contains only the components needed for Kubernetes. No package manager, no SSH daemon, and no accidental drift.
Declarative configuration – All node settings, including networking, storage, and Kubernetes components, are defined in a YAML machine config file and applied with talosctl.
Built-in security – The OS boots with a hardened kernel, runs all services as non-root, and supports TPM/secure boot where available.
Lightweight footprint – A typical Talos node uses < 150 MiB of RAM at idle, leaving more resources for workloads.
2. Preparing Hetzner VPS Instances
We’ll create three VPS instances: one control-plane node and two worker nodes. Each instance should have at least 2 vCPU, 4 GiB RAM, and two additional volumes (one for the OS image, one for persistent storage).
Create the instances in the Hetzner Cloud console. Choose the talos-linux image (available in the “Custom images” section) for the primary disk.
Attach extra volumes (e.g., 50 GiB each) that will later become storage pools for the cluster. Note the volume IDs – you’ll need them when configuring Talos.
Set a consistent naming scheme, for example:
k8s-control-01
k8s-worker-01
k8s-worker-02
Enable the Hetzner Cloud API token on your local workstation. This token is required for talosctl to communicate with the nodes.
3. Installing talosctl and Generating the Cluster Config
The talosctl binary is the only client you need on your workstation. It works on Linux, macOS, and Windows.
# Download the latest release (replace VERSION if needed)
curl -Lo talosctl https://github.com/siderolabs/talos/releases/download/v2.6.0/talosctl-$(uname -s)-$(uname -m)
chmod +x talosctl
sudo mv talosctl /usr/local/bin/
Generate a base configuration for a three-node cluster.
# Create a working directory
mkdir -p ~/talos && cd ~/talos
# Generate a config for a single control-plane node
talosctl gen config my-k8s-cluster https://api.hetzner.cloud/v1
The command creates two files:
controlplane.yaml – configuration for the control-plane node
worker.yaml – configuration for worker nodes
Open controlplane.yaml and edit the machine section to match the Hetzner instance name and the extra volume IDs that will be used for storage.
machine:
network:
hostname: k8s-control-01
# Use the internal IP for private networking
addresses:
- 10.0.0.2/24
disks:
# Primary disk (OS) remains untouched
- device: /dev/sda
partitions:
- mountpoint: /
size: 0
# Extra volume as a storage pool
- device: /dev/sdb
partitions:
- mountpoint: /var/lib/rook
size: 0
Repeat the same edits in worker.yaml, adjusting the hostname and IP address for each worker node.
4. Bootstrapping the Control-Plane Node
With the configuration ready, apply it to the control-plane VPS.
# Replace with the Hetzner ID of the control-plane VM
talosctl apply-config --insecure --nodes --file controlplane.yaml
What the command does:
--insecure – Skips TLS verification for the first boot; Talos generates its own certificates afterwards.
--nodes – Specifies the target machine(s) by Hetzner instance ID or IP.
--file – Points to the YAML config you edited.
After a few seconds, the node reboots into Talos. Verify that it’s up:
talosctl -n health
If the health check returns ready, the control-plane is operational. Retrieve the Kubernetes admin kubeconfig:
talosctl -n kubeconfig > ~/.kube/config
5. Adding Worker Nodes
Apply the worker configuration to each worker VPS in the same way:
# For worker-01
talosctl apply-config --insecure --nodes --file worker.yaml
# For worker-02
talosctl apply-config --insecure --nodes --file worker.yaml
Once both workers report ready via talosctl health, the cluster will automatically join them to the control-plane. Verify the node list with kubectl:
kubectl get nodes -o wide
6. Configuring Storage Pools with Rook Ceph
Talos runs containerd directly. For persistent storage, we’ll deploy the Rook operator, which provisions a Ceph cluster on the disks we attached earlier.
6.1 Install the Rook Operator
# Create a namespace for storage
kubectl create namespace rook-ceph
# Apply the Rook operator manifests
kubectl apply -f https://raw.githubusercontent.com/rook/rook/v1.13.5/cluster/crds.yaml
kubectl apply -f https://raw.githubusercontent.com/rook/rook/v1.13.5/cluster/operator.yaml -n rook-ceph
6.2 Create a CephCluster CRD that uses the attached disks
Create a file cephcluster.yaml with the following content. Adjust deviceFilter to match the device names you used (e.g., sdb for each node).
The operator will discover the /dev/sdb disks on each node, format them for Ceph, and start monitor, manager, and OSD daemons.
6.3 Verify the Ceph Cluster
# Wait a minute, then check the Ceph status
kubectl -n rook-ceph get cephcluster rook-ceph -o yaml | grep phase
# Expected output: phase: Ready
When the cluster is Ready, create a StorageClass for applications to use.
cat > rook-storageclass.yaml <
7. Deploying a Sample Application with Persistent Storage
To confirm that the storage pool works, deploy a simple NGINX instance that requests a persistent volume.
cat > nginx-pvc.yaml <
After the pod becomes Running, inspect the underlying Ceph volume:
kubectl get pvc nginx-pvc
kubectl describe pvc nginx-pvc
Conclusion
Combining Hetzner’s low-cost VPS offering with Talos Linux 2 gives you a secure, immutable Kubernetes platform that feels like a bare-metal deployment. The steps above cover the full lifecycle: provisioning Hetzner instances, installing Talos, bootstrapping a multi-node cluster, and configuring a Ceph-backed storage pool with Rook. Once the cluster is up, you can add more worker nodes, integrate monitoring stacks, or expose services with Ingress – all while enjoying the simplicity and reproducibility that Talos provides.
kubernetestalos linuxhetzner cloudrook cephstorageclasspersistent volumevps provisioningimmutable operating system
Try it on your own server
Follow along on a Cloud VPS with full root access, or read the step-by-step knowledge base guides.