Add bare-metal servers
This is for Hetzner's dedicated (bare-metal) servers, the ones you manage through Hetzner Robot. Adding one takes two steps. First, register it as a HetznerBareMetalHost object, so Syself Autopilot knows the machine exists. Second, add a worker pool (a machineDeployments entry) that uses the bare-metal class, so the cluster claims the machine.
If you have not bought and prepared the server yet, do Order and prepare a Robot server first.
Note
Syself Autopilot manages the software install and the adding and removing of machines from the cluster. It does not buy or sell the servers.
Create one HetznerBareMetalHost per server#
Create a baremetalhosts.yaml with one entry per machine and apply it to the management cluster. Each host needs its Hetzner serverID and the wwn of the disk to install onto. WWN (World Wide Name) is a unique ID for a disk.
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: HetznerBareMetalHost
metadata:
name: baremetal-1
spec:
description: My first baremetal machine
serverID: 1234567
rootDeviceHints:
wwn: eui.726163646f6d2d2d
maintenanceMode: false
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: HetznerBareMetalHost
metadata:
name: baremetal-2
spec:
description: My second baremetal machine
serverID: 7654321
rootDeviceHints:
wwn: eui.737472696e677349cd
maintenanceMode: false
maintenanceMode stays false for normal operation. Set it true only to pause Syself Autopilot's management of this one host, for example while granting Syself temporary access to the physical machine.
Warning
Newly purchased machines come with RAID enabled by default. Syself Autopilot never supports RAID: it always installs onto a plain disk. If a machine was previously configured with RAID, wipe the disks before adding it.
If you do not know the wwn, boot the server into the Hetzner rescue system (activate it on the Rescue tab in the Robot console, then reset the server on the Reset tab), and read the wwn-* symlinks:
$ ls -l /dev/disk/by-id/ | grep -E 'wwn|nvme'
The WWN column in lsblk is often blank in rescue, so /dev/disk/by-id/ is the reliable source. For the full rescue steps, see Serial console and rescue system .
Or omit rootDeviceHints entirely: the host stops with an error, and you read the WWN from the inventory it collected:
$ kubectl get hbmh <name> -o jsonpath='{.spec.status.hardwareDetails.storage}' | jq
On a multi-disk server, choose the disk you want the OS installed onto and put its WWN here; the other disks become data disks. Prefer the disk with the least capacity for the OS. It leaves the larger ones free for data storage. On a single-disk server, there is only one choice.
Label hosts and select them from a pool#
By default, any bare-metal pool can claim any free host. To steer specific machines into a specific cluster or pool, label the host and select it.
Add a label to the host:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: HetznerBareMetalHost
metadata:
name: baremetal-1
labels:
environment: production
spec:
serverID: 1234567
rootDeviceHints:
wwn: eui.726163646f6d2d2d
maintenanceMode: false
Then add a pool that selects it with workerHostSelectorBareMetal, under spec.topology.workers.machineDeployments:
- class: workeramd64baremetal
name: md-1
replicas: 1
variables:
overrides:
- name: workerHostSelectorBareMetal
value:
matchLabels:
environment: production
workerHostSelectorBareMetal also accepts matchExpressions (key, operator, values), for label conditions beyond an exact match. Set the variable once under spec.topology.variables to apply it to every bare-metal pool, or override it per pool as shown above. You can run several bare-metal pools in one cluster, each with its own selector. See Cluster variables for the full field list.
Tip
Always set matchLabels (or matchExpressions) on bare-metal pools. Without a selector, any available host can be pulled into the pool, including ones you labelled for another purpose.
Note
A selector narrow enough that no spare host matches also means a failed machine re-claims the same host when it is repaired. That is fine for a software fault, but a hardware fault returns after the reinstall. Alert on the hardware node conditions so a broken server surfaces instead of churning quietly; see Self-healing does not repair hardware .
Watch the provision#
$ kubectl get hbmh -w
$ kubectl get machines -o wide -w
-o wide shows the node name and provider ID next to each machine; bare-metal machines use an hrobot:// provider ID. The node has joined once the Machine reaches the Running phase.
A bare-metal worker node is ready in a few minutes. The machine boots the rescue system, the provisioner writes the sealed OS to the chosen disk, and the node reboots and joins the cluster.
Note
If a host stays stuck for a long time, the usual cause is the boot order: the server boots from its own disk instead of the network rescue system. Now and then Hetzner itself has trouble moving a server into rescue mode, which looks the same from the outside. Either way, see A server that will not provision .
The provisioner runs one pipeline inside the rescue system, and stops before the next step if any step fails:
- Preparation: checks that the target disk exists, is large enough, and passes a quick health and speed check.
- Image deployment: writes the sealed OS image to the disk, then verifies the written bytes against the build-time hash before trusting them.
- Bootstrap delivery: stages the files that differ per node, such as the kubeadm config, hostname, and SSH host keys.
- Seal: stages this node's settings into the sealed OS, verifies them, and writes the boot configuration.
- Handover: unmounts the disk and reboots into the installed OS.
What survives a reinstall#
Only the root disk is reinstalled. Data on the other disks survives a reprovision, which is what makes bare metal a good home for stateful workloads. To clear a data disk, wipe it yourself in the rescue system; see Wipe a disk .
Note
Within one pool (one machineDeployments entry), bare-metal hosts roll out one machine at a time. A change that replaces hosts (an image update, a class change) reprovisions them one after another, so the pool runs with fewer machines available until the rollout finishes.
This guarantee is per pool. If you split a workload across several pools, it weakens: pools currently roll one after another, but that ordering is not guaranteed to hold in the future. So keep a database's servers within a single pool.
A bare-metal pool scales like a cloud pool: change replicas and apply. See Scale a node pool for what happens on scale-down, or Remove a specific node to take one named machine out instead of letting Syself Autopilot pick. For why dedicated hardware and cloud VMs mix well in the same cluster, see Bare metal and cloud and bare-metal Kubernetes .