Blog ·
vSphere to Proxmox: what actually moves
The disk is the easy part of leaving VMware. The identity, the guest customisation, the network adapter and the guest agent are four separate problems.
By Provibr
Every migration guide for leaving VMware starts with the disk, because the disk is the part that has a file. Convert the VMDK, import it, boot it, done.
The disk is the easy part. What makes a vSphere machine work sits in four other places, none of them inside the VMDK: how the platform identifies the machine, how the guest is configured on first boot, which virtual hardware the guest has drivers for, and which agent inside the guest answers questions about it. Each is a different problem on Proxmox, and only one of them announces itself when it goes wrong.
This is what we found building read and write paths against both, measured against vCenter 6.7.0 build-24323669.
The disk, and the one call that is not there
There are two shapes of virtual disk in play. A running vSphere VM has a flat VMDK plus a small descriptor. An OVA or a downloaded cloud image has a stream-optimized VMDK: compressed, sequential and not bootable as it stands.
The obvious conversion path is CopyVirtualDisk_Task with a destination spec naming the target format. That call exists on a standalone ESXi host and does not exist on vCenter: it answers "The requested operation is not implemented by the server". It is a good example of a broader trap in this API, where the same operation exists at one management level and not at another.
Two things do work, and they are worth knowing in this order:
CopyVirtualDisk_Taskwithout a destination spec works on vCenter 6.7 and copies a disk preserving its type. On this vCenter it requires thedatacenterargument; the form with only a datastore path is refused.- The OVF import path (
CreateImportSpec→ImportVApp→HttpNfcLease→ HTTP POST →HttpNfcLeaseComplete) accepts a stream-optimized disk and converts on the way in. It needs only the.ovfdescriptor, not the whole OVA: an OVA is a tar archive with the descriptor at the front, retrievable with a single 128 kB range request.
On the Proxmox side the equivalent is qm disk import, which takes a VMDK and writes it into the target storage in that storage's own format. That part is genuinely straightforward.
Provisioning is modelled differently on the two sides. vSphere has two flags, thinProvisioned and eagerlyScrub, and they are not a three-way choice: thin excludes eager, so the valid states are thin, thick lazy zeroed and thick eager zeroed. Anything that models this as one enum with four values will eventually send an impossible combination.
Identity: the ID you have is not an identity
This one bites months later, not on migration day.
A vSphere object is addressed by a managed object reference, something like vm-101. It looks like an ID and is not one: it is a key in the database of that particular vCenter. Rebuild or restore that database and the reference does not follow the machine, and it gets handed out again to a different VM. Anything you stored six months ago now points at somebody else's machine, and the operation you were about to perform is a power-off.
The durable identifier is config.instanceUuid. Store that, and resolve the reference per operation through FindByUuid.
Proxmox has the opposite shape. A VM is a vmid: stable, human-readable, visible in every command, unique per cluster. That is nicer to work with, and it has its own consequence for a migration. You are choosing new identities, so every external system that referred to a machine by its vSphere identifier has to be re-pointed.
The location model differs too. With DRS enabled, the host a VM runs on changes without anyone asking, so a clone is given a resource pool and vSphere chooses. On Proxmox the node is the unit and moving between nodes is explicit. An inventory that recorded "which host" for a DRS cluster was recording something that changes on its own.
Guest customisation is a different mechanism, not a different syntax
Both platforms can configure a guest on first boot. They do it in ways that share no code.
On vSphere, the route that works without depending on the guest is guestinfo: four keys in extraConfig, read by cloud-init's VMware datasource.
| Key | Contents |
|---|---|
guestinfo.metadata | base64 of the metadata document, including the network configuration |
guestinfo.metadata.encoding | base64 |
guestinfo.userdata | base64 of the user-data document |
guestinfo.userdata.encoding | base64 |
The .encoding keys are not optional. Leave them out and cloud-init reads the base64 text as the content itself, and the machine comes up with a forty-character hostname of nonsense. No error, just a wrong answer.
The alternative, VMware's own CustomizeVM_Task, needs VMware Tools in the guest and on older Linux templates a working perl, which is the sort of requirement you discover when a provisioning run hangs halfway.
On Proxmox, customisation is a cloud-init drive attached to the VM, configured with a small set of keys: ipconfig0 through ipconfig3, nameserver, searchdomain, ciuser, cipassword, sshkeys, citype, ciupgrade.
One of those has a trap that stays invisible until a customer complains. ciupgrade has default = 1 in the Proxmox schema, so omitting it does not mean "do not upgrade packages on first boot"; it means "upgrade". An unchecked box has to be sent as an explicit ciupgrade=0 or the box is lying. It also does not exist before PVE 8.1, where sending it at all is a hard error rather than an ignored key.
Two more things go wrong the same way on both sides. The instance ID has to change when a machine gets a new identity: the iid-local01 from most examples means a cloned machine keeps the template's configuration. And the interface will not have the name you expect: ens160 on VMware, something else on KVM. Netplan with match: {name: "e*"} rather than a guessed device name is what makes one image work on both.
Virtual hardware the guest has drivers for
vmxnet3 is the sensible default network adapter on VMware, and it has a precondition: it needs VMware Tools in the guest. Install a template without Tools, give it a vmxnet3 card, and the operating system does not see a network card at all.
That matters twice. Before, because a template that boots fine on ESXi may need e1000e instead. And after, because Proxmox's high-performance choice is virtio, which needs its own drivers: present in modern Linux, absolutely not in a stock Windows image. e1000 is the slow, boring, works-everywhere option on both platforms, and it is the right first boot for a machine you are moving.
The disk controller has the same shape of problem: on Proxmox the fast path is virtio-scsi, which is again a driver question. We have not measured what changing controller type does to an already installed guest, and it is a common cause of a machine that migrates cleanly and then does not boot.
The agent inside the guest changes name
A surprising amount of what a panel displays does not come from the hypervisor at all.
On vSphere, guest.ipAddress comes from VMware Tools. So does real disk usage, through guest.disk: the datastore-level summary.storage.committed is the provisioning figure and reads wrong for a thin disk. Tools not running means those fields are empty, and empty means "I do not know", never "the machine has no address".
After the move the same information comes from qemu-guest-agent, a different package with a different name, which has to be installed. A fleet that migrates without it looks, in your own inventory, like a fleet that lost its addresses.
One further caution on that address: it arrives over the hypervisor's own channel to the guest, not over the network. It can be perfectly accurate for a machine whose card is attached to nothing. The only thing that proves reachability is reaching it.
Networking, and the connection that silently is not one
vSphere has two kinds of network backing and they do not look alike. A standard Network is referenced by name; a DistributedVirtualPortgroup by the UUID of its switch plus its own port group key.
Build only the first form and, on any environment with a distributed switch, you get a card attached to nothing. vCenter does not refuse it, because the name does exist as a network object. The machine boots without connectivity and the error is inside the guest.
Proxmox is simpler: a NIC names a bridge, with an optional VLAN tag. The mapping is yours to write; there is no automatic translation from a port group to a bridge. Two more things to decide before the first move:
- MAC addresses. Carrying them across keeps DHCP reservations and licence bindings valid, and means two machines share a MAC if the source is ever powered back on. Choose explicitly.
- The per-VM firewall. vSphere does not have one; that is NSX, a separate product. Proxmox does. Machines arriving from VMware have never had a hypervisor-level firewall, and turning one on is a project rather than a checkbox.
What does not travel, and is not in any file
Some things have no counterpart, and a plan that does not name them discovers them one at a time:
- Snapshots. The machine goes across as a flat disk; a snapshot tree is a vSphere structure with nothing to import it into.
- Changed block tracking, and with it any incremental backup history that depended on it.
- Tags, custom attributes and folders. These live in vCenter's database, not on the machine.
- Resource pools, DRS rules, reservations and shares. Proxmox has no equivalent scheduler, so a placement policy written as DRS rules becomes decisions about which node a machine sits on.
- VM hardware version. A vSphere concept with no Proxmox counterpart, so anything keyed on it in your automation is now dead code.
What our own tooling does, and does not, do
To be plain: we have not built vSphere to Proxmox migration. Our migration tooling covers exactly one route today, between two game panels, and it sits behind a feature flag that is off.
Two rules from building that one route are worth stealing anyway, because they make a migration reviewable rather than hopeful. The source is read-only, as a constant rather than a flag, so no configuration exists in which the tool writes to the system you are leaving. And a dry run is mandatory before an apply, with a count per category afterwards, source against destination. A migration that cannot tell you how many machines it moved has not finished; it has stopped.
This came out of building the vSphere and Proxmox integrations for Provibr, a server management panel for hosting providers, where both hypervisors have to be readable through one interface without either being described as the other. The setup notes are in the vSphere and Proxmox documentation, and Proxmox next to VMware is the platform comparison that usually starts this conversation; Proxmox next to oVirt is the same question when the answer has to stay open source.