Live migration moves a running guest from one host to another without a reboot. Memory, device state and vCPU registers travel; the guest’s idea of what CPU it is running on does not get a chance to change. That makes the virtual CPU model one of the first decisions to get right in any KVM cluster, and one of the most annoying to change later.

Why the CPU model matters for migration#

A guest discovers its processor through the CPUID instruction, usually once, early in boot. The kernel, glibc and applications then pick code paths based on what they found: AVX2 string functions, AES-NI crypto, specific speculation mitigations. Nothing tells them to look again after a migration.

If the destination host’s CPU lacks a feature the guest already uses, the next instruction that depends on it faults, and the guest or an application crashes. QEMU and libvirt therefore check before migrating that the destination can provide exactly the virtual CPU the guest was started with, and refuse if it cannot. The CPU model is how you control what that virtual CPU contains, and so which hosts can receive the VM.

Three ways to define a guest CPU#

libvirt’s domain XML has a <cpu> element with a mode attribute. QEMU has equivalents for two of the three common modes; host-model is a libvirt feature.

Mode libvirt XML QEMU What the guest sees Migration
Host passthrough <cpu mode='host-passthrough'/> -cpu host The host CPU, including features libvirt does not know about Only between identical hosts
Host model <cpu mode='host-model'/> (libvirt expands it to a named model plus features) A named model close to the host, with extra features added Safe; the expanded CPU must exist on the destination
Named (custom) <cpu mode='custom'><model>Haswell-noTSX</model></cpu> -cpu Haswell-noTSX A fixed model you choose Safe to any host that can run that model

Host passthrough gives the best performance and the full feature set. The QEMU documentation is direct about the cost: “Live migration is unsafe when this mode is used as libvirt / QEMU cannot guarantee a stable CPU is exposed to the guest across hosts.” libvirt adds that migration is dangerous unless source and destination are identical “in both hardware, QEMU version, microcode version and configuration”. libvirt’s migratable='on' attribute (libvirt 6.5 and later) strips features known to block migration, but it does not make mixed hardware safe. Use passthrough for single hosts, for workloads that need a specific instruction set and will not migrate, or for strictly uniform clusters.

Host model is libvirt’s middle ground. When the domain starts, libvirt copies the host-model CPU definition from the host’s domain capabilities into the running domain. During migration, according to the libvirt documentation, the “complete CPU model definition is transferred to the destination host so the migrated guest will see exactly the same CPU model for the running instance”, even if the destination is newer. The catch is the other direction: a guest started on a newer host carries that newer host’s features and cannot move to an older one. After a cold restart it may see a different CPU, depending on the host it boots on. OpenStack Nova uses host-model as its default cpu_mode for KVM on x86-64.

Named models are fixed CPU definitions that ship with QEMU and usually correspond to a hardware generation: Nehalem, Haswell-noTSX, Cascadelake-Server, EPYC-Rome, EPYC-Genoa, SapphireRapids and others. A VM with a named model sees the same CPU on every host that can provide it, which is what you want for a cluster.

<cpu mode='custom' match='exact' check='full'>
  <model fallback='forbid'>Cascadelake-Server</model>
  <feature policy='require' name='md-clear'/>
</cpu>

fallback='forbid' stops libvirt from substituting a different model if this one is unavailable. If you leave check out, libvirt picks the strictest value that keeps the virtual CPU from changing on migration.

There is also mode='maximum' (libvirt 7.1 and later), which enables every feature the accelerator supports. Like passthrough, it is not a migration-friendly choice.

Named models, versions and features#

List the models your QEMU knows:

qemu-system-x86_64 -cpu help

An excerpt from QEMU 8.2:

x86 Haswell               (alias configured by machine type)
x86 Haswell-noTSX         (alias of Haswell-v2)
x86 Nehalem               (alias configured by machine type)
x86 Nehalem-v1            Intel Core i7 9xx (Nehalem Class Core i7)
x86 Nehalem-v2            Intel Core i7 9xx (Nehalem Core i7, IBRS update)
x86 kvm64                 (alias configured by machine type)

Models are versioned. Nehalem-v2 is Nehalem-v1 with the IBRS speculation-control update. An unversioned name is an alias that the machine type resolves, so the same VM definition on the same machine type always gets the same CPU. The end of the -cpu help output lists the CPUID feature names you can add or remove on top of a model:

qemu-system-x86_64 -accel kvm -cpu Westmere,pcid=on ...

The QEMU CPU model guide lists the features worth adding when the host supports them, mostly speculative-execution mitigations: on Intel, spec-ctrl, stibp, ssbd, md-clear and mds-no among others; on AMD, ibpb, stibp, virt-ssbd and amd-ssbd. A named model alone may not expose these, so check the guide for your generation.

libvirt shows which named models this host can actually run under KVM:

virsh -c qemu:///system domcapabilities | grep "<model usable="
      <model usable='yes' vendor='unknown'>qemu64</model>
      <model usable='no' vendor='AMD'>phenom</model>
      <model usable='no' vendor='Intel'>n270</model>

usable='no' means the host CPU, the kernel or QEMU cannot provide every feature of that model. To see which features block a model, or what a model looks like once this host’s limitations are applied:

virsh -c qemu:///system hypervisor-cpu-baseline --model Haswell-noTSX

x86-64 microarchitecture levels#

The x86-64 psABI, the System V ABI document for x86-64, defines microarchitecture levels. Each level includes everything in the previous one:

Level Adds (per the x86-64 psABI) Roughly from
x86-64 (baseline, “v1”) CMOV, CX8, FPU, FXSR, MMX, OSFXSR, SCE, SSE, SSE2 All 64-bit x86
x86-64-v2 CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3 Intel Nehalem, AMD Bulldozer
x86-64-v3 AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, OSXSAVE Intel Haswell, AMD Zen (EPYC)
x86-64-v4 AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL Intel Skylake-SP (Xeon Scalable), AMD Zen 4 (EPYC Genoa)

These are compiler and distribution targets (gcc -march=x86-64-v3), not CPU products. Intel client processors from Alder Lake onward do not expose AVX-512, so they are v3 even though they are newer than Skylake-SP.

The QEMU CPU model guide includes a table of which named models satisfy which level. In summary: qemu64 and kvm64 are baseline only; Nehalem through IvyBridge reach v2; Haswell, Broadwell, Skylake-Client, EPYC and EPYC-Rome reach v3; Skylake-Server, Cascadelake-Server, Icelake-Server, SapphireRapids and EPYC-Genoa reach v4.

The levels matter for migration planning because operating systems now require them. With RHEL 10 (2025), Red Hat supports only x86-64-v3 and higher. AlmaLinux OS 10 ships v3 binaries by default and an additional x86-64-v2 build. A VM whose CPU model is below the level its OS was built for will not boot, or will crash when it reaches an unsupported instruction, even when the physical host is new enough. A cluster baseline of Nehalem keeps old hosts in the migration pool but rules out RHEL 10 guests in that cluster.

Check which levels a Linux host (or guest) supports with the glibc dynamic loader (glibc 2.33 or later):

/lib64/ld-linux-x86-64.so.2 --help | grep x86-64-v
  x86-64-v4
  x86-64-v3 (supported, searched)
  x86-64-v2 (supported, searched)

Run it inside a guest to see what its virtual CPU model exposes.

Finding a common baseline#

For a set of hosts, compute a CPU every host can run. Collect each host’s domain capabilities, then let libvirt work out the intersection:

# on each host
virsh -c qemu:///system domcapabilities > /tmp/$(hostname -s)-domcaps.xml

# on one host, after copying the files together
cat /tmp/*-domcaps.xml > all-hosts.xml
virsh -c qemu:///system hypervisor-cpu-baseline all-hosts.xml --migratable

The result is a <cpu mode='custom'> element with a named model and a feature list that you can place in domain XML. --migratable leaves out features that would block migration. hypervisor-cpu-baseline also takes into account what the hypervisor on the host where you run it can provide; the older cpu-baseline command does not. The virsh manual recommends feeding it CPU definitions from domcapabilities rather than from capabilities.

To check a candidate definition against a particular host before you move anything:

virsh -c qemu:///system hypervisor-cpu-compare candidate-cpu.xml

In practice, many teams round the computed result down to a plain named model (for example Cascadelake-Server or EPYC-Rome) plus the mitigation features, because a named model is easier to reason about and to compare across tools.

Mixed-generation clusters#

There are three common patterns, and real estates often combine them.

One baseline for the whole cluster. Choose the newest named model that every host can run and give every VM that model. VMware administrators know this as EVC. It is simple and keeps every host a valid migration target, but the whole cluster is held back to its oldest hardware. When the oldest hosts retire, raise the baseline; each VM picks up the new model only after a full power-off and power-on, because a running guest cannot change its CPU.

Group hosts by generation. Keep each generation in its own cluster or host group with its own baseline, and live-migrate within the group. Moving between groups becomes a planned cold migration. OpenStack Nova supports a variant of this: in custom mode, [libvirt] cpu_models takes an ordered list, oldest and most common first, and Nova picks the first model that provides the CPU traits a flavor or image asks for. The Nova documentation’s guidance for mixed hosts is to choose a model matching “the oldest of your compute node CPUs”.

Host model, one direction. With host-model, VMs can always move to newer hosts, but only move back to an older host if they were started on one. This works for rolling hardware refreshes when the old hosts are on their way out, and is a poor fit for clusters that stay mixed.

Keep Intel and AMD hosts in separate migration domains. Their named models are vendor-specific, and the only cross-vendor models (qemu64, kvm64) are baseline x86-64, below what current distributions require.

Practical guidance#

  1. Inventory each host’s CPU and its usable named models (virsh domcapabilities) before you build or extend a cluster, not after the first failed migration.
  2. Pick the baseline from the guests’ requirements as well as the hosts: if you run RHEL 10 or other v3-only systems, the baseline must be at least a v3 model such as Haswell-noTSX or EPYC.
  3. Use named models for anything that should live-migrate. Reserve host-passthrough for pinned, non-migrating workloads and identical hardware.
  4. Add the speculative-execution features the QEMU guide recommends for your generation, and confirm with /proc/cpuinfo in a test guest.
  5. Treat a baseline change as a maintenance event: VMs only see it after a full power cycle, so track which VMs are still on the old model.
  6. Test a live migration in both directions between the oldest and newest hosts after any QEMU, libvirt, kernel or microcode update.

Where Victor fits#

Victor Host, the host OS Victor ships, requires an x86-64-v2 CPU with VT-x or AMD-V. Victor Community sets a per-cluster CPU baseline (an EVC equivalent) from the QEMU named models every host in the cluster can run, applies it to a VM at its next power-on, and lets you add or mask CPU flags per VM within what every host supports. Its host join check refuses a host that cannot run the cluster’s baseline or comes from another CPU vendor.

See how Victor runs day-2 operations