User guide: CPU, memory, disk and NIC hotplug
How growing spec.resources on a running Machine works, and what today's
real limits are.
How it works
apiVersion: kairon.zyvor.dev/v1alpha1
kind: Machine
metadata:
name: db
spec:
resources: {cpu: "4", memory: "4Gi"} # was "2" / "2Gi"
# ... unchanged otherwise
Requesting more hotplug headroom
By default FluxVM reserves roughly double cpu, and memory + 2Gi or
double memory (whichever is larger), as hotplug headroom at boot time --
enough for most cases, but a fixed ceiling you can't grow later (see "Real
limits" below). If you know upfront you'll want to grow a Machine well
beyond that default, request more headroom explicitly at creation:
apiVersion: kairon.zyvor.dev/v1alpha1
kind: Machine
metadata:
name: db
spec:
resources:
cpu: "4"
memory: "4Gi"
maxCpu: "32" # reserve headroom for hotplugging up to 32 vCPUs
maxMemory: "128Gi" # reserve headroom for hotplugging up to 128Gi
# ... unchanged otherwise
maxCpu/maxMemory are creation-time-only, like cpu/memory's own
boot-time sizing -- unlike them, editing these on an already-running
Machine has no effect at all, since the headroom is baked into the VM's
boot-time -smp/-m arguments (maxcpus=/maxmem=) and there's no way
to grow that after boot. If maxCpu is set lower than cpu, FluxVM
silently raises it back up to at least cpu rather than erroring; if
maxMemory is set lower than memory, that's an inconsistent QEMU
argument FluxVM/QEMU itself will reject with an error, not something
Kairon validates first.
Just edit spec.resources on an already-Running Machine and increase
cpu/memory — kairon-node picks up the difference on its next reconcile
tick and calls FluxVM's real QMP hotplug (device_add/object-add), not a
reboot. status.appliedVCPUs/appliedMemoryMiB report what's actually been
realized so far.
Unlike every other spec.resources field, this one is live-reconciled
-- spec.image, spec.network, spec.cloudInit etc. remain creation-time-only.
Why Kairon tracks status.appliedVCPUs/appliedMemoryMiB itself
FluxVM has no endpoint to query a VM's current live vCPU/memory count --
hotplugged CPUs and DIMMs are pure QMP-runtime state, never written back
into FluxVM's own stored VM record. So kairon-node is the only source of
truth for how much of spec.resources has actually been realized: it
computes the delta between spec.resources and status.applied*, sends
only that delta to FluxVM's hotplug endpoints, and advances status.applied*
by the amount FluxVM reports actually landed.
Real limits today (v1 of this feature)
- Grow-only. FluxVM has no CPU/DIMM unplug. Lowering
spec.resourcesbelow what's already hotplugged is logged and ignored, not applied and not an error --status.applied*simply stays at its current (higher) value. To actually shrink a Machine, stop and recreate it. - Bounded by headroom reserved at creation.
spec.resources.maxCpu/.maxMemory(see "Requesting more hotplug headroom" above) requests more than FluxVM's own default headroom, but it's still a fixed ceiling set once at creation, not something a running Machine can grow. Once that headroom is exhausted, a hotplug request fails clearly (not enough hotplug headroom...), surfaced instatus.message, and retried automatically next tick (it won't succeed until the request is lowered back within headroom, or the Machine is stopped and recreated with a largermaxCpu/maxMemory). - Every hotplugged resource is lost across a stop/start cycle. Neither
hotplugged vCPUs nor hotplugged memory are part of the VM's boot-time
-smp/-marguments, so a cold migration, a manual stop/start, or any other Machine re-realization resetsstatus.applied*back down to plainspec.resources-- this is expected, not a bug, and mirrors real QEMU behavior (see FluxVM's own hotplug docs). - QEMU-only, since that's the only FluxVM backend with hotplug support.
Disk hotplug: spec.disks
spec.disks attaches PersistentVolumeClaims to a running Machine as SCSI
block disks. Add an entry and kairon-node hot-adds the disk on its next tick;
remove it and the disk is unplugged. The PVC and its data are never touched.
spec:
disks:
- name: data # [a-z0-9-], up to 32 chars, not "root"
claimName: db-data
or kaironctl disk attach db data --claim db-data /
kaironctl disk detach db data / kaironctl disk list db.
- The PV must be hostPath- or local-backed. A Block-mode PV attaches its
device path (e.g.
/dev/vg0/data); a Filesystem-mode PV attaches thedisk.imginside its directory, as for boot volumes. - A CSI-backed PV works too, with Kairon's own driver
(
csi.kairon.zyvor.dev, Filesystem mode) or any driver on the node's third-party allowlist (Filesystem or Block mode, with aVolumeAttachmentwhen the driver needs one). kairon-node stages and publishes the volume under<publishDir>/disks/<driver>/<machine>/<disk>, attaches it, and records it instatus.diskVolumes. Removing the disk detaches it and then unpublishes and unstages the volume; deleting the Machine does the same. The node needs the CSI setup from machine-storage-csi.md. - To point a CSI disk at another claim, remove the entry first and add it
back under the new claim once
status.diskVolumeshas dropped it. - Detach CSI disks before migrating a Machine. The volume is published on the source node only, and the destination does not take it over.
- In the guest the disk shows up as
/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_<name>; device letters are not stable, the serial is. - FluxVM's
policy.allowed_image_dirs, when set, must include the PV directories; block devices must live under/dev. status.attachedDiskslists what kairon-node attached. Only those are ever detached, so a disk attached to the VM by other means is left alone. A VM recreated from scratch (stop/start, cold migration) gets everyspec.disksentry re-attached.- QEMU's image locking refuses a disk another running VM already has open.
NIC hotplug and hot-unplug: spec.network.extraInterfaces
On a mode: tap Machine, spec.network.extraInterfaces adds up to three
bridged NICs next to the primary one, live:
spec:
network:
mode: tap
bridge: br0
extraInterfaces:
- name: lan
bridge: br-lan
- name: dmz
bridge: br-dmz
mac: 02:aa:bb:cc:dd:ee # optional
or kaironctl nic add fw lan --bridge br-lan / kaironctl nic remove fw lan
/ kaironctl nic list fw.
- Without
mac, the MAC is derived from the Machine UID and interface name, so it stays the same across restarts. - Removing an entry hot-unplugs that NIC and deletes its host tap. The guest must acknowledge the PCIe unplug within 10 seconds; Linux guests do.
- The primary NIC (
spec.network.bridge) can't be hot-removed. - NICs keep their PCIe slot when one in the middle is removed, so guest interface names of the others don't change.
status.attachedInterfacesrecords each NIC with the MAC used.
The MCP tools machine_disk and machine_nic (write, --allow-write) make
the same spec edits for agents.