VMware exits rarely fail at the copy step. They fail on something nobody wrote down: a guest cluster on a shared disk, a raw device mapping, a license agent pinned to a BIOS UUID, a Windows server with a static IP that nobody can reach after boot. The hard part is knowing, before the first maintenance window, which VMs will not move cleanly and why.
This runbook uses Victor’s tooling for the concrete steps, but the order of operations applies to any KVM destination.
Why the inventory comes first#
An inventory answers three questions you cannot answer any other way:
- What blocks a move? Features with no clean equivalent on the target, or that cannot be carried in a disk image.
- What does the target need? Host count, memory, and raw storage, with failure headroom.
- What does each workload depend on? Port groups, datastores, folder and resource pool structure, and the identifiers that guests and license servers check.
Done early, exceptions become decisions. Done late, they become outages.
What to export#
RVTools (now distributed by Dell; download it only from Dell, Robware.net or RVTools.com) exports the vCenter inventory to a workbook with File > Export all to Excel. Since 4.8.1 it refuses connections with certificate problems unless you override that, so expect to sort out vCenter’s certificate trust first.
Victor’s offline assessment reads these tabs:
| Tab | What the assessment takes from it |
|---|---|
vInfo (required) |
Name, datacenter, cluster, host, folder, resource pool, template flag, power state, CPUs, memory, provisioned and in-use MiB, guest OS, SMBIOS UUID, VM UUID, firmware, EFI Secure Boot, CBT, FT state, encryption, passthrough, annotation |
vDisk |
Each disk’s capacity, datastore path, raw LUN and sharing mode |
vSnapshot |
Snapshot names per VM |
vTools |
VMware Tools status |
Export all tabs anyway. vNetwork, vHost, vDatastore and the
distributed switch tabs are what you will use by hand when you design the
target network and storage, and the assessment does not cover vSAN policies,
NSX or distributed switch configuration at all. Those need their own review.
Run the assessment offline#
victorctl migrate assess needs no Victor server and no vCenter access when
given an RVTools file:
victorctl migrate assess estate.xlsx # every VM: ready, info, warn or block, and why
victorctl migrate assess estate.xlsx -o json | jq '.vms[] | select(.worst == "block")'
victorctl migrate assess estate.xlsx --report assessment.xlsx
If you can get a read-only vCenter account, a live scan reads the same facts plus details RVTools does not reliably give (BIOS and instance UUIDs, raw-mapping and sharing modes, passthrough devices, encryption). It changes nothing in vCenter:
victorctl migrate assess --vcenter https://vcsa.example.mil \
-u svc-assess@vsphere.local --vcenter-ca-file vmca.pem # password from the terminal or --password-stdin
Each VM gets a worst severity: ready, info, warn or block. The rules:
| Rule | Severity | What it means | How to resolve it |
|---|---|---|---|
rdm |
block | A raw device mapping cannot travel as a disk image | Convert the RDM to a VMDK in vSphere, or re-attach the LUN to the VM after migration |
multi-writer |
block | A shared disk, which means a guest cluster | Plan an outage, move the cluster’s nodes together, re-attach the shared disk on the target |
passthrough |
block | vGPU, DirectPath I/O or PCI passthrough | Remove the device before migration, add the GPU or device back on the target |
fault-tolerance |
block | FT is configured | Turn FT off; on Victor, protection is fenced HA restart |
vm-encryption |
block | vSphere VM encryption | Decrypt in vSphere first, then enable disk encryption on the target |
snapshots |
warn | Snapshots exist and are not carried over | Consolidate or delete them before migration |
cbt-off |
warn | Changed Block Tracking is off | Irrelevant for cold migration today; CBT matters only for a future warm path |
guest-os / guest-os-unknown |
warn | The OS is not on the virt-v2v conversion list, or nothing reported an OS | Test this VM in the pilot before scheduling it |
domain-controller |
warn | Name or notes look like a Windows DC | Its VM Generation ID is kept; do not revert pre-migration snapshots, and test-boot it isolated |
nested-pool |
warn | A nested resource pool (Prod/DB) |
Victor’s are flat; it becomes Prod-DB with the leaf’s limits, or map it explicitly |
duplicate-bios-uuid |
warn | Two VMs share a BIOS UUID (copied VM files) | Decide which keeps it; the others get a new UUID, and Windows may need reactivation |
secure-boot |
info | UEFI with Secure Boot | Expect to re-enable Secure Boot after migration (see below) |
vmware-tools |
info | Tools installed | Victor Guest Tools replace them; status and IP reporting resume after first boot |
Every block VM needs a decision before it can be scheduled.
Size the target#
The same command sizes the destination. The defaults assume a 64-core, 512 GiB host, 4 vCPUs per physical core, 20% of capacity kept free and Ceph with 3 replicas. Override them with your hardware:
victorctl migrate assess estate.xlsx --summary --host-cores 96 --host-memory 1024 \
--cpu-overcommit 3 --headroom 0.2 --replicas 3
How the numbers are built:
- Compute counts powered-on, non-template VMs only. Hosts for CPU and for memory are computed separately (memory with no overcommit), and the larger wins.
- Hosts is that number plus one, so HA can restart a failed host’s VMs (N+1), and never fewer than three, which Ceph needs.
- Ceph raw capacity is the provisioned size of every VM in the export, including powered-off VMs and templates, times the replica count, divided by the usable share. 100 TiB provisioned at 3 replicas with 20% kept free is 375 TiB raw.
Treat this as a planning floor: it ignores thin provisioning savings, peak load and growth.
Decide what not to migrate#
The cheapest VM to migrate is the one you retire. The assessment makes these candidates easy to find:
- Powered-off VMs and old templates. They count in the storage sizing. Confirm owners, archive what must be kept, delete the rest.
- Vendor appliances. Ask the vendor for a supported KVM image rather than converting the disk.
- Guest clusters and RDM-heavy databases. Rebuilding on the target with application-level replication is often simpler than moving shared disks.
- Stateless tiers deployed from pipelines. Redeploy them instead.
Map the estate in a plan#
Migration itself runs on the Victor server. Add vCenter as a source (Victor signs in before saving, and seals the password with its master key), then scan it:
victorctl migrate source add vcsa-east --vcenter https://vcsa.example.mil \
-u svc-migrate@vsphere.local --vcenter-ca-file vmca.pem
victorctl migrate source scan vcsa-east # every VM with its pre-flight verdict
Scanning needs a read-only role. Cold migration also needs
VirtualMachine.Provisioning.GetVmFiles and Datastore.Browse, plus
VirtualMachine.Interact.PowerOff if Victor may shut sources down.
A plan maps vSphere objects to Victor objects:
| vSphere | Victor | Flag | When unmapped |
|---|---|---|---|
| Port group (standard or distributed) | Network | --map-network |
--network, else a blocking finding |
| Datastore | Storage pool (KVM) or storage class (KubeVirt) | --map-datastore |
--datastore, else a blocking finding |
| VM folder | Folder | --map-folder |
Same path |
| Resource pool path | Project (KVM) or namespace (KubeVirt) | --map-pool |
--project, else default |
| Cluster | Target cluster and runtime | --cluster, --runtime |
Required |
victorctl migrate plan create wave-1 --source vcsa-east --cluster cl-a --project apps \
--map-network 'VM Network=vlan-204-web' --map-datastore datastore1=ceph-ssd \
--map-pool Prod/DB=data --vm web-01 --vm web-02 --vm db-01:2
victorctl migrate plan validate wave-1 # exit 3 when it doesn't validate
victorctl migrate plan export wave-1 > wave-1.yaml
--vm NAME:N puts a VM in wave N. Validation rescans the source and adds
plan-level checks: unmapped or missing targets, name collisions, two VMs
keeping one BIOS UUID, a storage pool without room, and a warning for VMs
that are still powered on.
The exported file holds what the plan asks for, never what Victor computed, so it belongs in Git. Review mapping changes in a pull request like any other infrastructure change, then apply:
victorctl migrate plan apply -f wave-1.yaml # creates the plan, or replaces the one with its name
Plans can also be managed with the Terraform resource victor_migration_plan
or the API. There is no console wizard for creating plans; the console shows
plans and their status.
Make identity decisions explicit#
Guests notice identity changes. Victor keeps, per VM, the BIOS UUID (what
dmidecode -s system-uuid returns), the instance UUID, the MAC addresses
and, on the KVM runtime, the VM Generation ID (KubeVirt has none). The VM
does get a new Victor managed-object ID; the plan records which source
moref became which Victor moref, so update any tooling that keyed on morefs.
Network identity is the other half. vmxnet3 becomes virtio, so a Linux
guest sees a new NIC name (ens192 becomes enp5s0) and its old
configuration no longer applies. For Linux guests, Victor writes each NIC’s
configuration into the guest’s own stack (netplan, NetworkManager,
systemd-networkd, ifupdown or wicked), matched by MAC, and pins the old name
with a systemd .link file so firewall rules that name ens192 keep
working. Choose a mode per plan or per NIC:
| Mode | Use it when |
|---|---|
auto (default) |
Keep static addresses Tools reported, DHCP where the guest used it |
keep |
The addresses must be exactly what Tools reported |
dhcp |
The guest should come up on DHCP |
pool |
Re-address from a Victor IP pool on the target network |
leave |
Do not touch the guest; required for guests that define bonds, bridges or VLANs |
victorctl migrate plan create db --source vcsa-east --cluster cl-a \
--network vlan-310-db --datastore ceph-ssd --network-identity keep --vm db-01 --nic 00:50:56:aa:bb:cc=leave
Two caveats. vCenter forgets guest IP data when a VM powers off, so scan
while VMs are running. Keeping static IPs in Windows guests requires
Enterprise; on Community, use dhcp or leave and set them after first
boot.
Pilot with representative VMs#
Pick five to ten VMs that cover your real spread: each Linux family you run, one with several disks, one with several NICs, one UEFI guest, one with a static IP, and one application you can actually test end to end.
Things the pilot will teach you:
- Shut down cleanly. A cold migration needs the source off. If the plan
sets
--power-off-source, Victor shuts the guest down through VMware Tools and powers it off after five minutes. A hard power-off can leave a file system journal that will not replay; the migration guide records a Rocky Linux 10 guest that stopped in the dracut emergency shell for exactly this reason, and migrated fine after a clean shutdown. - Secure Boot. An EFI source boots UEFI with Secure Boot off. Turn it on after confirming the guest boots with it.
- Disk space on the Victor server. Export and conversion use
--migration-dir, needing up to twice the disks of each VM migrating at once. - Real duration. Time each stage; that is your window estimate.
Community and Enterprise paths#
Community includes assessment, sources, plans with identity
preservation, and cold migration of one VM per call. Conversion uses
qemu-img (disk format only), which is enough for current Linux guests
because their kernels ship virtio drivers. Each VM is one durable task:
export, convert, import, create, boot and verify, then Guest Tools install.
victorctl migrate start wave-1 --vm web-01 --wait
victorctl migrate start wave-1 --vm web-02 --retry --wait # a failed VM again
Enterprise adds what larger exits need:
- Waves and bulk start (
--wave N, several--vm, or a whole plan). - virt-v2v conversion, which injects virtio drivers and removes VMware Tools, needed for Windows guests.
- Isolated test boot (KVM clusters only): a copy of each running source, taken through a temporary vCenter snapshot that is removed afterwards, boots with its identity on a network with no uplink, and checks run in the guest.
- Cutover one VM at a time in a window with pre and post hooks, respecting maintenance windows and change freezes, and rollback, which powers the vCenter source back on.
victorctl migrate test-boot wave-1 --wave 1 --check agent --check tcp:22 \
--check 'script:systemctl is-active nginx' --wait
victorctl migrate cutover wave-1 --wave 1 --pre-hook https://lb.example.mil/drain \
--post-script 'systemctl restart app' \
--not-before 2026-10-10T22:00:00Z --not-after 2026-10-11T02:00:00Z --wait
victorctl migrate rollback wave-1 --vm web-01
In both editions the source is never deleted.
Keep automation running with the vSphere bridge#
Scripts and pipelines written for vCenter do not all get rewritten on day
one. Victor can expose a vSphere-compatible API, off by default and enabled
with victord --vmware-bridge, that identifies as vCenter 8.0.3. Community
serves reads, power operations and host maintenance mode, which covers
govc and PowerCLI reports, Ansible inventory and Terraform data sources.
Enterprise adds write paths (clone, reconfigure, snapshots, create, destroy,
tags, guest operations), so the Terraform vsphere provider and Packer can
run unchanged. The project’s conformance suite passes 141 checks on
Community and 227 on Enterprise, with pinned clients: govc 0.56, PowerCLI
13.3, the vsphere Terraform provider 2.12.0 and the Packer plugin 2.5.0.
Test your own scripts against it.
Run both platforms side by side#
A cold migration leaves the source powered off but intact, so rollback is always possible by powering it back on. Plan for weeks or months of overlap:
- Keep vCenter licensed and backed up until the last wave has run in production for an agreed period.
- Give each migrated VM a backup job on the target before its source is retired.
- Never run source and target together: same MAC and IP on one network is an outage.
- Delete sources deliberately, wave by wave, after sign-off.
What is not available yet#
Be clear with stakeholders about current limits:
- Warm migration (CBT pre-copy with a short final sync) is planned, not available. Every migration today is cold: downtime is roughly export plus conversion plus import plus first boot.
- OVA/OVF import, adopt-in-place and migration reports are planned.
- Validation. Sources, scans and cold migration have been validated against vcsim, the vCenter simulator, not yet a real vCenter 7 or 8 or a standalone ESXi host. The NFC export path is untested against real vCenter.
- Windows. virt-v2v conversion has been tested with a stand-in, not end to end, so Windows migration is unproven.
- Tags, custom attributes and notes are not carried over. Test boot is refused on KubeVirt. Cutover webhooks are not signed.
Run your own pilot against your real vCenter before committing dates.
A phased checklist#
Phase 0: inventory
- Export RVTools (all tabs) from every vCenter.
- Run
victorctl migrate assessand save the--reportworkbook. - Review vSAN, NSX and distributed switch configuration separately.
Phase 1: decisions
- Resolve or accept every
blockfinding. - Mark VMs to retire, rebuild or leave on vSphere.
- Size hosts and Ceph with your hardware values.
- Record which applications depend on BIOS UUIDs, MACs or morefs.
Phase 2: plans
- Add sources, scan while VMs are running.
- Create plans with explicit network, datastore, folder and pool maps.
- Choose network identity modes; use
leavefor bonded or VLAN guests. - Export plans to YAML and review them in Git.
Phase 3: pilot
- Migrate representative VMs; time each stage.
- Verify identity, addresses, application health and backup.
- Rehearse rollback: target off, source powered back on.
Phase 4: waves
- Schedule by dependency group, smallest blast radius first.
- Shut sources down cleanly before each VM starts.
- Keep sources until sign-off, then delete deliberately.
Where Victor fits#
Victor covers the assess, plan and cold-migrate path in Community, and adds waves, virt-v2v, isolated test boot, cutover and rollback in Enterprise. Migrated VMs land in one inventory across KVM and KubeVirt. Validate it against your own vCenter first; that work has not been done for you yet.
See the migration path