Das Setup

Auf einem Proxmox-VE-8-Hypervisor (pve-deathstar) sollen mehrere Debian-10-VMs RAM zur Laufzeit anpassen können — Stop/Start nur für jede kleine Memory-Änderung nervt. Proxmox kann das, wenn man Hotplug + NUMA aktiviert:

qm set <VMID> --hotplug disk,network,usb,memory,cpu --numa 1

Beide Optionen sind Pending-Changes — die VM muss einmal sauber durchgestartet werden (Power-Off, nicht nur Reboot), damit QEMU mit den neuen Flags startet.

Das Symptom

Nach dem Restart bootet die VM mit nur 1024 MB sichtbarem RAM, obwohl in der Config memory: 6144 und balloon: 1024 steht. Im QEMU-Monitor (qm monitor <VMID>info balloon) liest sich das so:

balloon: actual=6066 max_mem=6144 total_mem=803 ...

QEMU sagt: 6 GB sind verfügbar (actual=6066). Der Gast sagt: ich sehe 803 MB (total_mem=803). Eine Diskrepanz von rund 5 GB — und die ist immer reproduzierbar exakt 1 GB sichtbares RAM, egal wie groß memory: konfiguriert ist.

Diagnose

Im Gast (Debian 10.13, Kernel 5.10.0-0.deb10.35-amd64):

dmesg | grep -i memory
[    0.025960] Memory: 261408K/1048032K available (...)
[    0.069461] x86/mm: Memory block size: 128MB

Der Kernel sieht beim Boot 1 GB. Das ist die Boot-Memory (DIMM 0). Alles darüber wird von Proxmox als Hot-Plug-DIMMs im 128-MB-Raster angehängt — sie sind technisch da, aber nicht aktiv:

ls /sys/devices/system/memory/
cat /sys/devices/system/memory/memory8/state
offline

Genau das ist der Punkt. Die zusätzlichen 5 GB hängen als memoryN-Devices im sysfs, alle im State offline.

Root Cause: Buster fehlt die udev-Regel

Debian 11 (Bullseye) und 12 (Bookworm) bringen out-of-the-box eine udev-Regel mit, die Hotplug-Memory beim Auftauchen automatisch online schaltet. Debian 10 (Buster) nicht. Der Kernel kann es, systemd-udev könnte es triggern — es fehlt schlicht die Regel-Datei.

Fix in zwei Zeilen:

cat > /etc/udev/rules.d/80-memory-hotplug.rules <<'EOF'
SUBSYSTEM=="memory", ACTION=="add", ATTR{state}="online"
EOF
udevadm control --reload

Ab dem nächsten Boot (oder dem nächsten Memory-Hotplug-Event) werden neue DIMMs automatisch online gestellt.

Quick-Fix für die laufende Session

Wer nicht direkt rebooten will, schaltet die schon angehängten Blöcke manuell on:

for m in /sys/devices/system/memory/memory*/state; do
  [ "$(cat $m)" = "offline" ] && echo online > "$m"
done

Verifikation:

free -m
              total        used        free
Mem:           5923         312        5421

Sechs Gigabyte da, wie konfiguriert.

Was jetzt geht — und was nicht

Mit funktionierendem Hotplug lässt sich RAM live nach oben ziehen:

qm set <VMID> --memory 8192

Greift sofort, ohne Neustart. Der Trick funktioniert aber nur in eine Richtung. Hier die Faustregel:

AktionLive möglich?Bemerkung
qm set --memory erhöhenJaSofort wirksam, neue DIMMs werden hot-plugged
qm set --memory verringernNeinPending-Change, braucht qm shutdown && qm start. Reboot im Gast reicht NICHT — der QEMU-Prozess läuft weiter
qm set --balloon ändernJaBalloon-Driver passt dynamisch an, zwischen balloon: und memory:
qm set --numa 1 aktivierenNeinPending, braucht Power-Off
qm set --hotplug ... ändernNeinPending, braucht Power-Off

Ein paar Caveats noch, die in der Proxmox-Doku nur am Rande stehen:

  1. NUMA ist Pflicht. Ohne numa: 1 ignoriert QEMU die Hotplug-Memory-Slots komplett. Das Flag im --hotplug-String allein reicht nicht.
  2. Ballooning + Hotplug schließen sich nicht aus. memory: definiert die Maximalgröße (Hotplug-Slots), balloon: das untere Limit. Der Balloon-Driver bewegt sich live in diesem Korridor.
  3. Reboot ≠ Power-Off. Solange qm shutdown nicht durchgelaufen ist, läuft QEMU mit den alten Flags weiter. Pending-Changes (NUMA-Aktivierung, Memory verkleinern, Hotplug-Optionen) werden erst beim echten Cold-Start gezogen.
  4. Memory-Block-Size ist Fix. 128 MB pro Block heißt: kleinere Schritte als 128 MB sind nicht sinnvoll, der Rest hängt sowieso bis zum nächsten Block-Rand.

Take-away

Wenn eine Debian-10-VM auf Proxmox mit Memory-Hotplug nur mit 1 GB bootet, ist nicht QEMU schuld und nicht die Config — es ist der Gast, dem die udev-Regel fehlt. Eine Zeile in /etc/udev/rules.d/80-memory-hotplug.rules löst das Problem dauerhaft. Bei Bullseye und neuer kommt die Regel mit dem Paket systemd mit; Buster ist die einzige Distro im aktuellen Park, wo man sie nachreichen muss — und genau das macht den Bug so unauffällig: Auf der einen VM funktioniert alles, auf der nächsten plötzlich nicht.

Bei Gelegenheit lohnt es sich, die VM auf Debian 12 zu heben — dann verschwindet auch dieser Workaround wieder.