Skip to content

Migration & balancing

NexoVirt can move a VM or container from one of your hosts to another, empty a node before maintenance, pick the best node for new guests and suggest moves when a node runs hot. All of this needs the hosts.manage permission.

A migration moves a guest between two separate hosts in NexoVirt (two standalone nodes, or a node and a cluster). It uses Proxmox’s remote_migrate, so the guest’s disks are copied over the network to the target. Moves between nodes of the same Proxmox cluster are not offered here; use Proxmox’s own migration for those.

Guest stateHow it moves
Running VM (QEMU)Live: keeps running, with a brief pause at the final switchover.
Running container (LXC)Restarts once: stopped for the transfer, started on the target.
Stopped guestOffline.
  • Both nodes run Proxmox VE 7.3 or newer.
  • The source node can reach the target’s Proxmox API port (TCP 8006). The panel tests this through the source node’s agent before starting.
  • A current node agent on the source node (for the reachability test) and, if port-forwards move along, on the target node.
  • The target node is online and not in maintenance mode.

The guest itself must not:

  • be a template,
  • have snapshots (delete them first),
  • have PCI or USB passthrough devices or a mounted ISO image,
  • use settings only root@pam may apply on the target (LXC devN entries, raw lxc.* keys, bind mounts),
  • have more than one IP address managed by the panel,
  • be busy with another Proxmox task or panel action.
  1. Open the guest’s page and click Migrate.
  2. Choose the Target node. The dialog then loads the plan for that node.
  3. Review the plan:
    • Summary: data to copy, Expected duration and the check count.
    • Mapping: each disk storage and network bridge mapped to one on the target. Storages and bridges with the same name are pre-selected.
    • Network: the IP plan and port-forward plan (see below).
    • Needs attention lists blocking problems and warnings; Checks passed lists the rest.
  4. Click Migrate. You land on the live progress page.

Expected duration is the average of earlier successful migrations between the same two nodes. Without any earlier migration on that route it shows “Duration unknown”. The dialog also warns how long the guest will be stopped (containers, stopped guests) or that only a brief pause is expected (live VMs).

If the guest has an IP address from an IP zone:

  • The address is kept when a zone on the target covers it and it is free there.
  • Otherwise choose an IP zone on the target, and the guest gets the next free address from it.

Guests with a DHCP or manually set address are left untouched.

Port-forwards pointing at the guest move to the target node’s NAT. If a public port is already taken there, the dialog shows the New public port it will use. Tick Drop these port-forwards instead of moving them if you do not need them on the target. Moving forwards needs NAT configured on the target node.

The progress page shows each step and a live log. When the migration is done it takes you to the guest on its new host.

  • The panel creates a short-lived API token on the target for the transfer and deletes it afterwards.
  • After the transfer the panel applies the IP plan, moves the port-forwards and re-points the panel records (owner, plan and so on) to the new guest. Problems in these steps finish the migration as Done with warnings instead of failing it.
  • If a migration fails after the data was already transferred, Retry continues from there instead of copying again.

The original guest stays on the source node as a stopped safety copy (tagged nexovirt-migrated, autostart off). Its page shows a Migration safety copy notice with the date it will be deleted. Every action on it is refused except Delete safety copy now.

Safety copies are deleted automatically after the retention period (by the daily metrics:prune run). Running migrations and safety copies are skipped by plan enforcement and alerting.

Settings → Migration:

  • Safety copy retention (days): how long the stopped original is kept. Default 7.
  • Bandwidth limit (MiB/s): caps the transfer. Empty uses the Proxmox default.

Migrations in the sidebar lists every migration, newest first, with source, target, mode, status, start time, duration, data copied, average speed and the safety copy state. Filter by status (Running, Done, Done with warnings, Failed) or by node / cluster. Select rows and use Delete safety copies now to remove several safety copies at once.

Maintenance mode keeps new guests and incoming migrations off a host. Turn it on or off on the node page under Settings → Operations → Maintenance mode.

While it is on, the host is left out of the create wizard and of automatic placement, guest creation on it is refused (panel, reseller portal and API), and it is not offered as a migration target. Existing guests keep running.

Evacuate (node page, Settings → Operations) moves the guests of a host to one target node:

  1. Click Evacuate. The dialog lists the host’s guests with a pre-flight result, an estimate and the IP plan for each.
  2. Choose the Target node / cluster and Target node, and an IP zone on the target when addresses cannot be kept. Tick Drop port-forwards that cannot move if needed.
  3. Select the guests and click Start evacuation.

Starting an evacuation turns maintenance mode on for the source host. The guests then migrate one at a time; a guest whose pre-flight fails is skipped with its reason. Templates, safety copies and busy guests are never offered. Each original stays as a safety copy, as with a single migration.

The progress page (View progress on the node page) refreshes every 5 seconds and shows each guest’s status and reason. Stop skips the guests that have not started yet; maintenance mode stays on until you turn it off.

When creating a guest, the host option Automatic (recommended) lets NexoVirt pick the node. It uses the panel’s inventory and metrics only (no live Proxmox calls) and leaves out nodes that are:

  • offline or in maintenance mode,
  • without a healthy, current agent,
  • without a storage of the right content type that has room for the disk,
  • without enough free RAM,
  • not assigned to the reseller (for reseller creates).

The remaining nodes are ranked by a weighted score of free RAM after placement, idle CPU (average of the last 30 minutes) and free storage after placement. Ties go to the node with fewer guests. The choice is shown with its explanation and checked again before the guest is created.

The same logic is available through the API: node=auto on POST /api/v1/hosts/{id}/guests, the host-less POST /api/v1/guests, and GET /api/v1/placement/suggest. See the REST API reference.

Settings → Placement → Automatic placement:

  • Preselect “Automatic (recommended)” in the create wizard.
  • RAM weight, CPU weight, Storage weight: only the ratio matters. Defaults: RAM 0.5, CPU 0.3, storage 0.2.

Balance in the sidebar shows every node with its guest count and marks nodes Above threshold (hot) or in Maintenance. Below it, Suggestions lists proposed moves:

  • A node is hot when its RAM usage is above the RAM threshold or its average CPU over the last 30 minutes is above the CPU threshold.
  • For each hot node, running guests are considered smallest first. Each suggestion goes to the best node that is not hot and stays under the thresholds after the move, and must pass the migration pre-flight.
  • Suggestions stop once the hot node would be back under the thresholds.

Each suggestion shows the guest, from, to, what it frees, the expected downtime and the IP plan. Recalculate refreshes the list. Select suggestions and click Start selected to create a balance run: the moves run one at a time like an evacuation, with the same progress page, but no host is put into maintenance mode.

Nothing is moved automatically: balancing only happens when you start a run.

Settings → Placement → Load balancing:

  • RAM threshold (%): default 80.
  • CPU threshold (%): default 85.
  • Max suggestions: per Balance page load, default 10.

Migrations, evacuations, balance runs and failovers are executed by php spark migrate:work, in its own queue so a long transfer never blocks guest provisioning. The installer’s cron setup runs it every minute. If migrations stay queued, check that this cron job is in place. See CLI & daemons.