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:
| Aktion | Live möglich? | Bemerkung |
|---|---|---|
qm set --memory erhöhen | Ja | Sofort wirksam, neue DIMMs werden hot-plugged |
qm set --memory verringern | Nein | Pending-Change, braucht qm shutdown && qm start. Reboot im Gast reicht NICHT — der QEMU-Prozess läuft weiter |
qm set --balloon ändern | Ja | Balloon-Driver passt dynamisch an, zwischen balloon: und memory: |
qm set --numa 1 aktivieren | Nein | Pending, braucht Power-Off |
qm set --hotplug ... ändern | Nein | Pending, braucht Power-Off |
Ein paar Caveats noch, die in der Proxmox-Doku nur am Rande stehen:
- NUMA ist Pflicht. Ohne
numa: 1ignoriert QEMU die Hotplug-Memory-Slots komplett. Das Flag im--hotplug-String allein reicht nicht. - 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. - Reboot ≠ Power-Off. Solange
qm shutdownnicht 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. - 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.