Skip to content

Migrate from Virtualizor

The Virtualizor importer reads a Virtualizor KVM node’s own database and recreates its users, plans, storage pools, IP pools, virtual machines, disks and IPs in VirtConsole. Nothing is copied over the network: every disk stays on the node. File-based qcow2 disks are moved on the same filesystem, and LVM/ZFS volumes are renamed on the same volume group or pool - both by commands the importer prints for you.

The import runs in two halves. The agent on the Virtualizor node extracts everything to JSON files. The management server then reads those files and writes the records in one transaction, so a failed import leaves nothing behind.

  • A working VirtConsole management server with a license that covers the imported hosts. See Management server installation.
  • The Virtualizor node runs KVM. OpenVZ, LXC and Xen nodes are not supported.
  • Install the VirtConsole agent on the Virtualizor node itself, following Hypervisor installation. This is the one case where the agent is installed next to another control panel: the extractor needs Virtualizor’s configuration at /usr/local/virtualizor/universal.php and its MySQL socket. Keep Virtualizor running during the extraction. The two do not share ports.
  • Add the node under Infrastructure > Hypervisors so it shows Online. Do not add storage pools or subnets for it by hand; the importer creates them.
  • Plan a maintenance window for the cutover. Virtual machines must be stopped in Virtualizor before their disks are moved and before they are started from VirtConsole.
  • No Virtualizor customer email is already registered in VirtConsole. A user whose email already exists is skipped, but that user’s VMs are still imported and end up owned by an account that does not exist, invisible to every customer. Rename or delete the clashing VirtConsole account first, or change the email in Virtualizor.
  • A fresh backup of the management server, taken with vcli app:backup, so a bad import can be rolled back by restoring the database.

SSH into the node and run:

Terminal window
vcli import:virtualizor

The command connects to the Virtualizor database, probes each storage pool (lvs for LVM thin and thick, zfs list for ZFS), and writes one JSON file per resource under the agent home directory in import/virtualizor/. It ends with a report:

=== Virtualizor Import Report ===
Resource Count
Storage 2
Subnets 3
Users 41
Plans 6
Instances 58
Disks 61
Ips 74
Interfaces 58
Storage pool types (native, no conversion):
LVM thin (native): 1
File qcow2 (native): 1

Useful options:

  • vcli import:virtualizor --report prints the report again from the existing files without touching Virtualizor.
  • vcli import:virtualizor --clean deletes the extracted files. Run it after a successful import, or before extracting again.

Watch for “Skipping” lines. A VPS whose owner or storage pool is missing in Virtualizor is left out, and so are its disks and IPs.

On the management server, run the importer against the hypervisor by its panel name or ID with --dry-run:

Terminal window
vcli import:virtualizor node-01 --dry-run

The management server fetches the JSON over the agent’s API and prints a preview: a count per resource, the storage pool types found, and how many disks are native LVM or ZFS. No changes are made. Fix anything the preview flags (missing users, unexpected pool types) on the Virtualizor side and extract again.

Run the same command without --dry-run and answer the confirmation prompt:

Terminal window
vcli import:virtualizor node-01

The import writes, in order, users, plans, storage pools, subnets, instances, disks, IPs and network interfaces, all inside one database transaction. If any step fails the transaction rolls back and the panel is unchanged. When it succeeds it prints the counts and, for file-based disks, a rename list (see the next step).

What the import creates:

Virtualizor VirtConsole Notes
User User (customer) Same email and name. A random password is set; the customer resets it with Forgot password. Active flag kept.
Plan Instance plan CPU, RAM, disk, bandwidth, CPU topology, NIC type, I/O mode and network speed limits carried over. Storage type set to SSD.
Storage Storage pool, attached to the node, disabled File pools keep their path. LVM pools keep the volume group and thin pool; ZFS pools keep the zpool and dataset prefix.
IP pool Subnet, attached to the node Public or private from the pool’s internal flag, routed or bridged from its routing flag, IPv4 or IPv6, bridge name kept.
VPS Instance, stopped Named h<vpsid> (the old Virtualizor domain used its own vps_name, for example v1127, not the VPS id). CPU, RAM, topology, VNC port kept (VNC password is re-encrypted; a Virtualizor VPS with no console password gets a fresh random one), suspended and network-suspended flags kept. Plan linked, OS matched to a catalog image when possible (see below).
Disk Instance disk LVM and ZFS volumes: path kept, format raw. File disks: new qcow2 path under the pool, see the rename list.
IP IP on the subnet, assigned to the instance Primary flag and MAC kept. Unassigned pool IPs are imported as free.
VPS network One public interface, plus one private interface when the VPS had an internal IP MAC kept on the public interface.

The import also matches each VM’s reported OS against the VirtConsole image catalog: Virtualizor exports an OS name like debian-13.4-x86_64, and the importer looks for the closest catalog match (exact version first, then the same major version). On a match it finds or adds the corresponding catalog image, the same way the admin image browser’s Add button would, enables it if it was disabled, and links it to the instance. This is what lets a matched instance show its real OS instead of “unknown”, and it also supplies a cloud-init default for Step 7 below. When nothing matches, the import prints “instance <name>: OS <os_name> has no catalog match; set the image by hand” and leaves the image unset - set it yourself under the instance’s Settings.

Not migrated: OS templates and ISOs, backups, Virtualizor firewall rules, bandwidth history, and billing links (WHMCS and similar). Re-point your billing module at VirtConsole after the cutover.

Virtualizor stores qcow2 files under its own naming; VirtConsole expects <pool path>/<instance id>/<disk id>.qcow2. For every such disk the import prints the commands to run on the node:

=== File-Based Disk Renames ===
mkdir -p /vz/kvm/6e1c…/
mv /vz/kvm/v1001-abc.img /vz/kvm/6e1c…/9f42….qcow2

LVM and ZFS disks are not adopted at Virtualizor’s own volume or dataset name. VirtConsole always derives its own block-device path - /dev/<volume group>/<prefix><disk id> for LVM, /dev/zvol/<pool>/<dataset prefix>/<disk id> for ZFS - every time it reads the disk, including the first boot, so the volume or dataset has to be renamed to match. The import prints one line per disk:

=== Block Device Renames ===
Stop the VM in Virtualizor first. The rename is metadata-only and instant; Virtualizor cannot start the old domain afterwards, which is the cut-over.
lvrename vg_data v1001-abc 9f42…
zfs rename tank/v1002-old tank/vsv/a83e…

Stop the corresponding VM in Virtualizor before running any of these lines, then run the printed mkdir/mv commands for file-based disks and the printed lvrename/zfs rename commands for LVM/ZFS disks on the node. All of them operate on the same storage the disk already lives on, so each takes seconds regardless of disk size - but the LVM/ZFS rename is also the point of no return: once renamed, Virtualizor can no longer start the old domain for that disk.

  1. Stop every imported VM in Virtualizor, then stop the Virtualizor services so they cannot start the old Virtualizor domains again (virsh list on the node shows their names - Virtualizor names a domain after its own vps_name, for example v1127, not the VPS id). Leave the agent running.
  2. In the admin panel go to Infrastructure > Storage and enable the imported pools.
  3. Open Instances, pick an imported instance and click Start. The agent defines a new libvirt domain from the imported disks and interfaces, with the same MAC and IPs, so the guest comes up with its old network identity.
  4. Check the console and network of the first few instances before starting the rest.
  5. Tell customers to use Forgot password on the VirtConsole login page.

Once every instance runs from VirtConsole, remove Virtualizor from the node and run vcli import:virtualizor --clean on it.

Step 6: Install the guest agent on every imported machine

Section titled “Step 6: Install the guest agent on every imported machine”

One command on the node does Steps 6 and 7 together, offline, on the stopped disk: vcli import:prepare <instance> (for example vcli import:prepare h1001). For a Linux guest it needs no other arguments; for a Windows guest pass --windows-tools <path to virtio-win-guest-tools.exe> and --cloudbase-msi <path to CloudbaseInitSetup.msi>. Add --dry-run to see the commands it would run first. Skip ahead to Step 7’s panel side once it finishes, or do the same steps by hand instead:

Virtualizor VMs do not carry the QEMU guest agent by default. Install it inside each imported guest:

  • Debian/Ubuntu: apt-get install -y qemu-guest-agent && systemctl enable --now qemu-guest-agent
  • RHEL family (AlmaLinux, Rocky, CentOS Stream): dnf install -y qemu-guest-agent && systemctl enable --now qemu-guest-agent
  • Windows: install virtio-win-guest-tools.exe from the virtio-win ISO (fedorapeople.org), which adds the VirtIO serial driver and the QEMU Guest Agent service; verify with Get-Service QEMU-GA.

The agent is what makes Reset Root/Administrator Password, Web SSH, network changes pushed from the panel, Docker one-click apps and Windows activation work on an imported instance. Imported machines keep whatever init system Virtualizor gave them and need no cloud-init for any of the above - see QEMU guest agent for the full reference and troubleshooting.

Step 7: Enable cloud-init (Linux) or cloudbase-init (Windows)

Section titled “Step 7: Enable cloud-init (Linux) or cloudbase-init (Windows)”

If you already ran vcli import:prepare <instance> in Step 6, the guest side below is done - go straight to the panel side.

An instance whose OS was matched to a catalog image (see Step 3) inherits that image’s own cloud-init flag, so nothing here is needed for it. The panel toggles below are only for an instance the import could not match - it has no image to inherit from, so the configuration drive defaults to off. Enable it if you also want VirtConsole to push SSH keys, custom user-data, or network changes that survive without the guest agent’s live push.

Guest side

  • Linux: apt-get install -y cloud-init (Debian/Ubuntu) or dnf install -y cloud-init (RHEL family), and make sure the NoCloud datasource is allowed - check /etc/cloud/cloud.cfg.d/ for a datasource_list override; the distro default list already includes it. Before the Stop/Start below, run cloud-init clean --logs so cloud-init treats the next boot as a first boot and re-reads the new configuration drive.
  • Windows: install cloudbase-init, and in cloudbase-init.conf set metadata_services=cloudbaseinit.metadata.services.configdrive.ConfigDriveService and keep the default plugin list (including UserDataPlugin, SetHostNamePlugin, NetworkConfigPlugin and SetUserPasswordPlugin) enabled - the service runs at every boot. VirtConsole builds a config-2 labelled ISO for Windows guests, the same format the ConfigDrive service reads.

Panel side

In the admin panel, on the instance’s manage page, open Settings:

  1. For a Windows guest, turn on Enable Windows Settings under Windows / Hyper-V Enlightenments first - with no image row, this flag is how the platform tells a Windows guest apart on an imported machine, and it decides whether the configuration drive is built as a cloudbase-init config-2 ISO or a cloud-init NoCloud seed.
  2. Under Cloud-init Support, set the configuration drive to Enabled. An imported instance has no image to inherit from, so Inherit from image resolves to off and no drive is ever attached.

Apply it

Stop the instance and start it again (not Restart) so the configuration drive is built and attached. From then on, hostname, root/administrator password, SSH keys, network configuration and user scripts set from the panel apply on every boot.

What needs which

Feature Guest agent Cloud-init / cloudbase-init
Password reset, Web SSH, Docker apps, Windows activation Required Not used
Static IP / network change pushed live, Windows Required Not used
Static IP / network change pushed live, Linux Required Required (reruns cloud-init init --local through the agent)
SSH keys and custom user-data applied on first boot Not used Required

Imported instances behave like any other: power actions, console, reinstall, backups, firewall and migrations all work. Plans imported from Virtualizor are ordinary instance plans; edit or retire them under Compute > Plans. See QEMU guest agent for what else the agent unlocks day to day.

  • “Failed to find Virtualizor config at /usr/local/virtualizor/universal.php”. The agent is not on the Virtualizor node, or Virtualizor is not a KVM install at the default path.
  • “Import folder does not exist. Run php artisan import:virtualizor first.” The management server asked the node before the extraction ran. Run step 1 on the node.
  • “Hypervisor ‘name’ not found”. The name or ID does not match a host under Infrastructure > Hypervisors. Copy it from the hypervisor page.
  • “Skipping user x@example.com: already exists” in the output. That email is already a VirtConsole account, so the user was not created and the VMs that belonged to it are now owned by an account that does not exist. Restore the backup you took before importing, rename or delete the existing VirtConsole account (or change the email in Virtualizor), and run the import again.
  • “LVM probe failed for VG” or “ZFS probe found no volumes”. The extractor could not read the pool with lvs or zfs list and fell back to the thick variant. Install lvm2 and thin-provisioning-tools, or zfsutils-linux, on the node and extract again.
  • An instance starts but has no network. If the import printed a “bridge not set in Virtualizor” notice for the subnet, it already defaulted the bridge to Virtualizor’s own viifbr0 - check the node actually uses that bridge name, or set the correct one on the subnet under Networking > Subnets and start the instance again.
  • The import fails half way. Nothing was written; the transaction rolled back. Read the error, fix the source data or extract again, and re-run.