Skip to main content
By the end of this page a VM has moved to a different host while it was running, and you will have checked the one thing that actually matters: that a connection open before the move was still the same connection after it. This page starts where Run a VM ended. It is also what you do before draining a host for maintenance.

What this changes on your cluster

A VirtualMachineInstanceMigration object, and then the VM’s node. Nothing about the VM’s definition changes — not its disks, not its address, not its name. Migration is the one operation here that alters where something runs without altering what it is.

Before you start

The VM must be migratable, and that was decided when it was created. Celum reports it as migratable, derived from the VM’s eviction strategy: LiveMigrate means yes, anything else means no. Two create-time choices put a VM on the wrong side of that line:
  • a ReadWriteOnce image — the disk cannot be attached on two nodes at once, so the VM cannot move;
  • networkMode: bridge — which deliberately clears the eviction strategy, because an eviction strategy on a VM that cannot migrate would wedge a node drain.
Neither can be changed on a running VM. If migration matters, it is a decision made on Run a VM, not here. You also need vm:Migrate, scoped supervisor:<name>:vm:<vm>.

The shape of it

Check it can move → start it → let the address follow → prove the session survived. The last step is the only one that distinguishes a live migration from a fast restart.

Step 1 — Start the migration

The VM detail page → ⋯ (More actions) → Live migrate. It starts at once, with no confirmation, and leaves the destination to the scheduler; pin a node through the API.
A running VM's detail page with the More actions menu open, listing Stop, Live migrate and Expose under Lifecycle, then Edit VM class, Move to project, Clone to project, View YAML and Delete

Live migrate is in the VM's More actions menu

A named targetNode is validated before it is used. A value that is not a real node on this supervisor is refused with 400 targetNode "…" is not a node on this supervisor. That check exists because an invalid node selector does not fail loudly: KubeVirt floods the namespace with “Error creating pod … invalid nodeSelector” warnings while the migration simply never starts. Pinning a target is worth doing when you are draining a specific host, and pointless otherwise.
Result: 200 {"status": "migrating", "migration": "<vm>-migration-…"}. The migration is running, not finished.

Step 2 — What happens to the address

If the VM has an identity address, the claim has to move at exactly the right instant, and the interesting part is that both obvious moments are wrong: The signal Celum actually uses is targetNodeDomainReadyTimestamp on the VMI, stamped the second the guest resumes on the target. It is the only moment that is neither early nor late.
A related trap if you are watching this yourself: status.nodeName lags. It is updated by the target’s virt-handler up to ~30 s after the migration completes, so during that window it still names the old host. Read the migration state, not the node name.

Verify — the session, not the VM

That the VM is Running on a new host proves almost nothing; a restart would look the same from a distance. Celum’s own end-to-end suite moves every VM onto every schedulable host and then asks four questions of a connection that was open the whole time:
  1. Exactly one distinct connection was used for the heartbeat. Two means it was re-established.
  2. The connection id did not change. A different id is a new session wearing the old one’s name.
  3. No gap longer than 15 s. A longer stall is a migration that technically succeeded and practically dropped the app.
  4. The sequence advanced. This is the one people leave out: without it you have proved the session survived idle, not that it kept working through every hop.
New connections succeeding after the move is a weaker check, and one the per-hop HTTP probes already cover. The point of a live migration is the connections that were already open.

Reading a migration that has not landed

POST …/migrate answers 200 when the migration object was created. That says nothing about whether the VM moved, and for a long time there was no way to ask: the only options were polling the VM list until its node changed, or a timeout — and a timeout reports the same way whatever caused it. GET /api/supervisors/{name}/vms/{vm}/migrate is that read. Most of it echoes objects you could fetch yourself — phase, source and target node, the target pod, timestamps, inFlight, elapsedSeconds, and the history of every earlier hop. One field does not:
blocked is why this endpoint exists. A migration that cannot be scheduled emits no VMI event at all, so the phase Scheduling looks identical one second in and twenty minutes in. The scheduler’s own sentence lives on the target pod’s PodScheduled condition, and the migration state names that pod, so the lookup is exact rather than guessed from the VM’s name.It usually reads something like 0/N nodes are available: N Insufficient memory — because the guest’s full RAM must be free on the target while the source still holds its copy. That is the failure most often misread as a storage problem. It was investigated twice this year and both times the search started on the storage side.
elapsedSeconds is there to be compared against KubeVirt’s completionTimeoutPerGiB, 150 s by default: a migration well inside that window is slow, not stuck. pinnedTo sits beside targetNode deliberately. One is what the caller asked for and the other is where KubeVirt went; they differ exactly when a pin was not honoured.

Cancelling, and the migration that blocks all the others

DELETE /api/supervisors/{name}/vms/{vm}/migrate aborts an in-flight migration; the VM stays where it is.
A stuck migration blocks every future one. KubeVirt refuses a new migration for a VMI that already has one outstanding — “in-flight migration detected” — so a migration that can never be scheduled, typically because it was pinned to a node that cannot take it, quietly makes the VM unmovable forever. Cancel removes exactly the migrations for this VMI that have not Succeeded, which is the repair for that state.
If a VM refuses to migrate and nothing looks wrong, check for a leftover migration object before checking anything else.

What commonly goes wrong

Next

VM lifecycle

The rest of the verbs — start, stop, resize, snapshot, clone, move between projects.

VM identity pools

The addresses that follow a VM, and how to re-address a stale claim.

Infrastructure & nodes

The hosts you are migrating between, and what each has left.

Troubleshooting clusters

When the problem turns out to be the node rather than the VM.