//Technology

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
Build a Bare‑Metal Kubernetes Cluster on Hetzner with Talos Linux

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

  1. Create the instances in the Hetzner Cloud console. Choose the talos-linux image (available in the “Custom images” section) for the primary disk.
  2. 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.
  3. Set a consistent naming scheme, for example:
    • k8s-control-01
    • k8s-worker-01
    • k8s-worker-02
  4. 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).

apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: false
  storage:
    useAllNodes: true
    useAllDevices: false
    deviceFilter: sdb
  dashboard:
    enabled: true
    urlPrefix: /
    port: 8443

Apply the manifest:

kubectl apply -f cephcluster.yaml

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.

Guides: VPS for WordPress · Node.js hosting · VPS or dedicated server · Paying by UPI