Run NUMA-aligned workloads
On a server with more than one CPU socket, each socket has its own local RAM. A pod reads its own socket’s RAM fast, but reaching the other socket’s RAM is slower because the data has to travel across the link between them. If a pod runs on one socket while its memory sits on the other, every memory access pays that extra cost. It is easy to miss, since nothing fails, the pod is just slower.
NUMA alignment keeps a pod's CPUs and its memory on the same socket. Syself Autopilot does this automatically for pods that qualify, so a latency-sensitive database or real-time service does not need any special operator or annotation. It only needs to request its resources correctly.
flowchart LR
subgraph S0["Socket 0 / NUMA node 0"]
P["sample-pod<br/>pinned to CPUs 8-11"]:::app
M0["local RAM"]:::platform
end
subgraph S1["Socket 1 / NUMA node 1"]
M1["local RAM"]:::platform
end
S0 <-->|"interconnect (the tax)"| S1Where it applies
Alignment is on by default for bare-metal workers and only matters on servers with two or more sockets. Hetzner Cloud VMs and control-plane nodes are single-socket machines where every pod schedules on one NUMA node, so there is nothing to align. When a pod qualifies, Syself Autopilot aligns it; every other pod runs exactly as it would without the feature.
Make a pod qualify
A pod is a candidate when both of these are true:
- Guaranteed QoS: every container sets
requestsequal tolimitsfor bothcpuandmemory. - Whole-integer CPU: the CPU value is a whole number (
cpu: "4"), not a fraction (cpu: "3500m"). A fractional Guaranteed pod runs fine, but it draws from the shared CPU pool and gets no exclusive cores, so it cannot be pinned.
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
# Force bare metal if the cluster also has cloud workers:
# nodeSelector: { instance.hetzner.cloud/is-root-server: "true" }
containers:
- name: app
image: your/image
resources:
requests: {cpu: "4", memory: 8Gi}
limits: {cpu: "4", memory: 8Gi}
The Topology Manager aligns each container on its own, not the pod as a whole, so two containers in the same pod can land on different sockets. Size any single container to fit within one socket; a request larger than a socket cannot be aligned, though it still runs.
How alignment behaves
Alignment is best-effort and never rejects a pod: it aligns when the pod fits inside a socket and admits it anyway when it does not. Exclusive whole cores leave the shared pool, so fewer pods fit onto the node; reserve this for workloads that measurably benefit. Once threads are pinned, the Linux kernel migrates the pod's memory pages to the socket they run on.
One pod, before and after
The same pod behaves in two very different ways depending on one manifest value. Before alignment, the pod can use every core on the node, so its threads may run on either socket. After alignment, it is restricted to a small, fixed set of cores that all belong to one NUMA node (the socket 0 cores shown in the diagram above). Nothing else in the manifest changes; setting the CPU request to a whole number (4 instead of 3500m) is what makes the pod eligible for pinning.
Verify pinning
Read the allowed CPU set from inside the pod:
$ kubectl exec sample-pod -- grep Cpus_allowed_list /proc/self/status
Cpus_allowed_list: 8-11
A short list such as 8-11 indicates a pinned pod; the full node range (0-63) indicates an unaligned one. To confirm those CPUs belong to a single NUMA node, SSH to the node and compare the list against /sys/devices/system/node/node*/cpulist and the kubelet's /var/lib/kubelet/cpu_manager_state. The pod's CPUs should fall entirely inside one node's cpulist.
The same alignment applies to latency-sensitive virtual machines run with KubeVirt , which reserve their CPU and memory just like a Guaranteed pod.
Share a GPU across pods
A slice is a scheduling token, not a fraction of the card. Learn when co-scheduling is safe and how to keep a whole GPU to one workload.
Run virtual machines with KubeVirt
Run full virtual machines next to your containers with KubeVirt on bare-metal nodes, and plan for node churn so a VM disk backed by local NVMe survives.