Keep Hetzner Cloud VMs on separate physical hosts, so one hardware failure never takes down more than one node. A Hetzner Cloud **spread** placement group does this: Hetzner never puts two VMs from the same group on the same physical machine. Without a placement group, Hetzner Cloud picks a physical host for each VM on its own. Two control-plane VMs could land on the same host. If that host fails, both VMs go down together, and the cluster can lose quorum (the majority of control-plane nodes that must stay up for the cluster to keep working). A worker pool without a group faces the same risk: one host failure can take out several of its nodes at once. A placement group only decides which physical host a node runs on. It does not decide which node a pod runs on; for that, see [Steer workload placement](/docs/hetzner/apalla/workloads/placement/steer-workload-placement). > [!NOTE] > Placement groups apply to Hetzner Cloud VMs only. Bare-metal servers are already separate physical machines, so there is nothing to spread: skip placement groups for bare-metal pools. ## Prerequisites - Access to the management cluster (or the GitOps repository that syncs to it) and a `Cluster` object to edit. - A pool that runs on Hetzner Cloud VM nodes; see [Add cloud servers](/docs/hetzner/apalla/servers-and-nodes/provision/add-cloud-servers) if you have not created one. ## Configure the groups Declare the groups Add `hcloudPlacementGroups` to `spec.topology.variables`. Hetzner Cloud supports only the `spread` type, and a spread group holds **at most 10 servers** (see the [Hetzner documentation](https://docs.hetzner.com/cloud/placement-groups/overview)). ```yaml - name: hcloudPlacementGroups value: - name: myPlacementGroup1 type: spread - name: myPlacementGroup2 type: spread ``` > [!NOTE] > The name you set here is not the name Hetzner shows. In the Hetzner Cloud console the group appears prefixed with the cluster name, as `-myPlacementGroup1`. Names are scoped per cluster, so two clusters in the same Hetzner project can safely reuse the same name: each cluster gets its own separate group, and the 10-server cap applies per cluster, not across the project. Assign one to the control plane Set `controlPlanePlacementGroupNameHcloud` to one of the names from step 1. This protects quorum by keeping control-plane nodes off the same host; see [High-availability control plane](/docs/hetzner/apalla/clusters/configure/high-availability-control-plane) for control-plane sizing. ```yaml - name: controlPlanePlacementGroupNameHcloud value: myPlacementGroup2 ``` Assign per-pool groups for workers Set `workerMachinePlacementGroupNameHcloud` in a worker pool's `variables.overrides`. Give each pool its own group so a host failure costs at most one node per pool: ```yaml machineDeployments: - class: workeramd64hcloud name: md-0 replicas: 5 failureDomain: nbg1 variables: overrides: - name: workerMachineTypeHcloud value: cpx42 - name: workerMachinePlacementGroupNameHcloud value: myPlacementGroup1 ``` > [!IMPORTANT] > A spread group holds at most 10 servers, so a worker pool that uses one is capped at 10 nodes. That fits a control plane (three or five nodes) and small worker pools well. A pool that needs to grow past 10 either runs without a group, or splits into several pools of up to 10, each with its own group. > [!TIP] > Run several worker pools and give each its own group. Each group spreads independently, so a pool of compute-dedicated (`ccx`) nodes can have its own group, separate from your general-purpose worker pool. ## Variable reference | Variable | Type | Required | Default | | --------------------------------------- | --------------------------------- | -------- | ------- | | `hcloudPlacementGroups` | array of objects (`name`, `type`) | no | none | | `controlPlanePlacementGroupNameHcloud` | string | no | none | | `workerMachinePlacementGroupNameHcloud` | string | no | none | See the [cluster variables reference](/docs/hetzner/apalla/reference/cluster-variables) for every variable a cluster supports. ## Complete example ```yaml vars title="cluster.yaml" apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: mycluster spec: clusterNetwork: services: cidrBlocks: ["10.128.0.0/12"] pods: cidrBlocks: ["192.168.0.0/16"] serviceDomain: "cluster.local" topology: classRef: name: hetzner-apalla-1-36-v3 version: v1.36.4 controlPlane: replicas: 3 variables: - name: hcloudPlacementGroups value: - name: myPlacementGroup1 type: spread - name: myPlacementGroup2 type: spread - name: controlPlanePlacementGroupNameHcloud value: myPlacementGroup2 workers: machineDeployments: - class: workeramd64hcloud name: md-0 replicas: 5 failureDomain: nbg1 variables: overrides: - name: workerMachineTypeHcloud value: cpx42 - name: workerMachinePlacementGroupNameHcloud value: myPlacementGroup1 ``` Placement groups lower the odds of losing more than one node at once. Self-healing handles whatever a host failure still takes down: it detects the unhealthy node and replaces it, see [Self-healing and node replacement](/docs/hetzner/apalla/concepts/operations/self-healing-and-node-replacement). ## Related - [Add cloud servers](/docs/hetzner/apalla/servers-and-nodes/provision/add-cloud-servers) - [High-availability control plane](/docs/hetzner/apalla/clusters/configure/high-availability-control-plane) - [Self-Healing and Node Replacement](/docs/hetzner/apalla/concepts/operations/self-healing-and-node-replacement) - [Cluster variables](/docs/hetzner/apalla/reference/cluster-variables)