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](/docs/hetzner/apalla/servers-and-nodes/provision/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.
```yaml title="baremetalhosts.yaml"
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](/docs/hetzner/apalla/support/grant-syself-baremetal-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](/docs/hetzner/apalla/servers-and-nodes/maintenance/wipe-a-disk) 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](https://robot.hetzner.com/), then reset the server on the **Reset** tab), and read the `wwn-*` symlinks:
```console
$ 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](/docs/hetzner/apalla/servers-and-nodes/access/serial-console-and-rescue).
Or omit `rootDeviceHints` entirely: the host stops with an error, and you read the WWN from the inventory it collected:
```console
$ kubectl get hbmh -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:
```yaml title="baremetalhost.yaml"
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`:
```yaml
- 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](/docs/hetzner/apalla/reference/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]
> If a pool's selector matches only one host, a failed machine will be reprovisioned on that same host after repair. Reprovisioning fixes software problems, but it cannot fix faulty hardware, so a hardware failure can return after the reinstall. Monitor [node conditions](/docs/hetzner/apalla/servers-and-nodes/access/node-problem-detection-and-conditions) to detect these failures. See [Self-healing does not repair hardware](/docs/hetzner/apalla/servers-and-nodes/maintenance/machine-health-checks-and-remediation#self-healing-does-not-repair-hardware).
## Watch the provision
```console
$ 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](/docs/hetzner/apalla/servers-and-nodes/access/server-not-provisioning).
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](/docs/hetzner/apalla/servers-and-nodes/maintenance/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](/docs/hetzner/apalla/servers-and-nodes/pools/scale-a-pool) for what happens on scale-down, or [Remove a specific node](/docs/hetzner/apalla/servers-and-nodes/maintenance/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](/docs/hetzner/apalla/concepts/internals/bare-metal-and-cloud) and [bare-metal Kubernetes](/docs/hetzner/apalla/concepts/internals/bare-metal-and-cloud).
## Related
- [Order and prepare a Robot (bare-metal) server](/docs/hetzner/apalla/servers-and-nodes/provision/order-and-prepare-a-robot-server)
- [Wipe a disk before provisioning](/docs/hetzner/apalla/servers-and-nodes/maintenance/wipe-a-disk)
- [A server that will not provision](/docs/hetzner/apalla/servers-and-nodes/access/server-not-provisioning)
- [Remove a specific node](/docs/hetzner/apalla/servers-and-nodes/maintenance/remove-a-specific-node)