Updated: 22 October 2026 · Applies to: SolusVM (historical reference; Virtualizor is this project's current VPS platform)
SolusVM is no longer this project's VPS platform. VPS hosting here now runs on Virtualizor, which does not need this manual LVM-snapshot-and-rsync procedure. This article is kept as historical reference for anyone still operating an older SolusVM-based node; it is not a procedure used on current infrastructure.
This gradual migration method suits a VM with a large disk (where a normal SolusVM migration would take many hours) or one that cannot tolerate a long downtime.
I. Prepare the destination node and VM
- On the destination node, create a VM identical to the source VM. The disk size must match exactly; other specs should match as closely as possible. Give it a different, temporary IP address.
- Boot the new destination VM once to confirm it boots cleanly.
- Shut it down again, and keep it shut down for the rest of this process.
- Make sure the source node can SSH to the destination node as root without a password, ideally via SSH keys:
ssh-keygen -t rsa
Copy the source node's/root/.ssh/id_rsa.pubinto the destination node's/root/.ssh/authorized_keys, then test it. For example, with source nodesource-nodeand destination nodedest-node, fromsource-node:ssh -v -l root -p 22222 dest-node
You should land ondest-nodewithout a password prompt.
II. Image a snapshot of the source VM to the destination VM
- On the destination node, find the destination VM's allocated logical volume (LV). Open its SolusVM-assigned VM ID's config file, for example
VMID_DST:cd /home/xen/VMID_DST cat VMID_DST.cfg
Find thediskline, for example:disk = ['phy:/dev/vg_destnode/VMID_DST_img,xvda1,w', 'phy:/dev/vg_destnode/VMID_DST_swap,xvda2,w']
Note the image path (here,/dev/vg_destnode/VMID_DST_img) as the "destination LV". - On the source node, do the same for the source VM's config, for example
VMID_SRC:cd /home/xen/VMID_SRC cat VMID_SRC.cfg
disk = ['phy:/dev/VolGroup00/VMID_SRC_img,xvda1,w', 'phy:/dev/VolGroup00/VMID_SRC_swap,xvda2,w']
Note the image path (here,/dev/VolGroup00/VMID_SRC_img) as the "source LV". - On the source node, create an LVM snapshot of the source LV:
lvcreate -s -n /dev/VolGroup00/VMID_SRC_snap -L 10G /dev/VolGroup00/VMID_SRC_img lvs
Confirm the new snapshot appears in thelvsoutput. - Image the snapshot across to the destination LV over SSH. Run this inside
screenortmux, since it can take hours depending on the LV's size. On the source node:screen dd if=/dev/VolGroup00/VMID_SRC_snap | ssh -p 22222 dest-node "dd of=/dev/vg_destnode/VMID_DST_img"
Use the destination node's internal (private) interface IP address in place of its hostname here if possible. While this runs, checktopon both nodes to confirm theddprocesses are active, or watch bandwidth graphs for both nodes; the source VM keeps running normally throughout. When it finishes, you should see a summary like:671088640+0 records in 671088640+0 records out 343597383680 bytes (344 GB) copied, 37518.1 seconds, 9.2 MB/s
Confirm there were no errors; if there were, fix the cause and restart the imaging. - Mount the destination LV to confirm the image copied correctly. On the destination node:
mount /dev/vg_destnode/VMID_DST_img /home/xen/VMID_DST/mnt df -h cd /home/xen/VMID_DST/mnt
Check that the files roughly match the source VM. - Once confirmed, remove the snapshot on the source node:
lvremove VolGroup00/VMID_SRC_snap
At this point you have two identical VMs: the source VM still running normally with no downtime, and the destination VM shut down, holding a point-in-time copy of the source's data.
III. Regularly sync the source VM to the destination VM
- Confirm the destination node can SSH directly into the source VM itself (not the source node) with key-based auth:
ssh-keygen -t rsa
Copy the destination node'sid_rsa.pubinto the source VM'sauthorized_keys, then test, for example with destination nodedest-nodeand source VMVMID_SRC:ssh -v -l root -p 22222 VMID_SRC
- Create a sync script on the destination node, saved as
/usr/local/scripts/sync-vm.sh:#!/bin/sh # Change this according to node type and VM config locations node_type=xen # xen/kvm vm_home=/home/$node_type rsync=/usr/bin/rsync ssh=/usr/bin/ssh ssh_port=22222 rsync_args="-ave '$ssh -p $ssh_port' --exclude='backup/' --exclude='sys/' --exclude='proc/' --delete --force" if [[ -z "$1" || -z "$2" ]]; then echo "Usage: $0 <Live Source VM IP Address (internal IP recommended)> <Dormant Destination VM ID>" echo exit fi src_vm_host=$1 dst_vm_id=$2 dst_vm_data=$vm_home/$dst_vm_id/mnt/ if [ ! -d "$dst_vm_data/home" ]; then echo "ERROR: Destination VM data ( $dst_vm_data ) does NOT exist. Make sure it is typed and mounted properly." echo exit fi echo "-------------------------------------------------------------------" echo "Begin $src_vm_host VM Sync to $dst_vm_data: "`date` echo "-------------------------------------------------------------------" eval $rsync $rsync_args $src_vm_host:/ $dst_vm_data echo "-------------------------------------------------------------------" echo "End $src_vm_host VM Sync to $dst_vm_data: "`date` echo "-------------------------------------------------------------------" echo - Test it from the destination node, inside
screen:screen /usr/local/scripts/sync-vm.sh VMID_SRC VMID_DST
It should sync the currently running source VM to the dormant destination VM, printing a begin and end line with timestamps. - Once it works, schedule it to run during off-peak hours via
/etc/crontab:0 19,0,5 * * * root /usr/local/scripts/sync-vm.sh VMID_SRC VMID_DST >> /var/log/sync-vm.log 2>&1
Adjust the schedule (here, 7pm, midnight and 5am) to the VM's own quiet hours. - Check on it periodically:
tail -f /var/log/sync-vm.log
IV. Prepare for the switchover
- Confirm the destination node can SSH into the source node (not the VM this time) with key-based auth, the same way as in section I, then test it.
- Create a pre-switchover sync script on the destination node, saved as
/usr/local/scripts/sync-vm-offline.sh. It is similar to the script above, but assumes both VMs are shut down during the sync:#!/bin/sh # Change this according to node type and VM config locations node_type=xen # xen/kvm vm_home=/home/$node_type rsync=/usr/bin/rsync ssh=/usr/bin/ssh ssh_port=22222 rsync_args="-ave '$ssh -p $ssh_port' --exclude='backup/' --exclude='sys/' --exclude='proc/' --delete --force" if [[ -z "$1" || -z "$2" ]]; then echo "Usage: $0 <Source node IP Address (internal IP recommended)> <Dormant Source VM ID> <Dormant Destination VM ID>" echo exit fi src_node_host=$1 src_vm_id=$2 dst_vm_id=$3 src_vm_data=$vm_home/$src_vm_id/mnt/ dst_vm_data=$vm_home/$dst_vm_id/mnt/ if $ssh -p $ssh_port $src_node_host test -d $src_vm_data/home; then echo "OK: Source VM data ( $src_vm_data ) exists and mounted." echo else echo "ERROR: Source VM data ( $src_vm_data ) does NOT exist. Make sure it is typed and mounted properly." echo exit fi if [ ! -d "$dst_vm_data/home" ]; then echo "ERROR: Destination VM data ( $dst_vm_data ) does NOT exist. Make sure it is typed and mounted properly." echo exit fi - Test run it on the destination node to confirm it runs without errors:
/usr/local/scripts/sync-vm-offline.sh
- Run it again with real arguments. Since the source VM's data is not actually mounted at this point, it should correctly error out, which confirms the script itself is ready:
/usr/local/scripts/sync-vm-offline.sh source-node.example.com VMID_SRC VMID_DST
Expect:ERROR: Source VM data ( /home/xen/VMID_SRC/mnt/ ) does NOT exist.Getting that error here means you are ready for the switchover.
V. Switch over to the destination VM
- Schedule a short downtime window (around an hour) with whoever owns the VM, and remove the regular sync job from
/etc/crontabon the destination node. - At the start of the downtime window, shut down the source VM in SolusVM. Confirm both VMs are shut down.
- On the source node, mount the source LV you noted in section II:
mount /dev/VolGroup00/VMID_SRC_img /home/xen/VMID_SRC/mnt df -h cd /home/xen/VMID_SRC/mnt
Confirm the files look right. - From the destination node, run the offline sync script for the final, up-to-date copy:
/usr/local/scripts/sync-vm-offline.sh source-node.example.com VMID_SRC VMID_DST
This copies the latest changes from the source VM to the destination VM. - Once it finishes, unmount both sides:
umount /home/xen/VMID_SRC/mnt umount /home/xen/VMID_DST/mnt
Confirm withdf -hthat neither mount remains. - In SolusVM, note the source VM's main and additional IP addresses, then remove them, replacing the source VM's IP with a single temporary address.
- In SolusVM, remove the destination VM's current IP allocations and assign it the exact IP addresses the source VM used to have. Boot the destination VM and confirm it comes up correctly on the new node, with every IP working and every service running.
- Leave the source VM dormant for a while rather than removing it immediately, until you are confident the destination VM is stable on the new node.