This guide covers the complete restoration process for a KVM/QEMU virtual machine using libvirt (virt-manager/virsh) from backup files.
vm-disk. qcow2— Virtual machine disk imagevm-config.xml— VM configuration exported from libvirtvm_VARS.fd— UEFI NVRAM file (only if VM uses UEFI firmware)
# Verify libvirt is installed
sudo systemctl status libvirtd
# Install if missing
sudo apt install qemu-kvm libvirt-daemon-system virt-manager # Debian/Ubuntu
sudo dnf install qemu-kvm libvirt virt-manager # FedoraIf restoring on a different machine, copy files from backup location:
# Example: Copy from remote backup server
rsync -avP user@backup-server:/path/to/backups/vm-disk.qcow2 ~/
rsync -avP user@backup-server:/path/to/backups/vm-config.xml ~/
rsync -avP user@backup-server:/path/to/backups/vm_VARS.fd ~/sudo mv vm-disk.qcow2 /var/lib/libvirt/images/
sudo chown libvirt-qemu:kvm /var/lib/libvirt/images/vm-disk. qcow2
sudo chmod 644 /var/lib/libvirt/images/vm-disk. qcow2qemu-img check /var/lib/libvirt/images/vm-disk.qcow2
qemu-img info /var/lib/libvirt/images/vm-disk.qcow2nano vm-config.xmlFind the <disk> section and update the path:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/vm-disk.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>Change the <source file='...'/> to match your actual disk location.
If your VM uses UEFI, find the <os> section:
<os>
<type arch='x86_64' machine='pc-q35-8. 0'>hvm</type>
<loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
<nvram>/var/lib/libvirt/qemu/nvram/vm_VARS.fd</nvram>
<boot dev='hd'/>
</os>Update the <nvram> path to:
<nvram>/var/lib/libvirt/qemu/nvram/vm_VARS.fd</nvram>Check the <interface> section:
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
</interface>Options:
network='default'— Uses libvirt's default NAT networktype='bridge'+source bridge='br0'— If using bridged networking, verify bridge name exists:
ip link show | grep brIf the bridge name differs, update it in XML or switch to network='default'.
To avoid conflicts if restoring on the same host with a different VM name, remove or regenerate:
<!-- Remove or change these lines -->
<uuid>12345678-1234-1234-1234-123456789abc</uuid>
<name>old-vm-name</name>Change <name> to your desired VM name:
<name>restored-vm</name>Let libvirt generate a new UUID by removing the <uuid> line entirely, or generate one:
uuidgensudo mv vm_VARS.fd /var/lib/libvirt/qemu/nvram/
sudo chown libvirt-qemu:kvm /var/lib/libvirt/qemu/nvram/vm_VARS.fd
sudo chmod 600 /var/lib/libvirt/qemu/nvram/vm_VARS.fdThe filename in /var/lib/libvirt/qemu/nvram/ must match exactly what's in the <nvram> tag in your XML.
virsh define vm-config.xmlExpected output:
Domain 'restored-vm' defined from vm-config.xml
virsh list --allYou should see your VM in the list with state "shut off".
virsh start restored-vmvirt-manager- Find your VM in the list
- Right-click → Open or Start
virsh dominfo restored-vmvirt-viewer restored-vmOr via virt-manager: Double-click the VM to open console.
Check permissions:
sudo ls -lZ /var/lib/libvirt/images/vm-disk.qcow2Fix with:
sudo chown libvirt-qemu:kvm /var/lib/libvirt/images/vm-disk.qcow2
sudo chmod 644 /var/lib/libvirt/images/vm-disk. qcow2If using SELinux (Fedora/RHEL):
sudo restorecon -R /var/lib/libvirt/images/Causes:
- Missing NVRAM file
- Incorrect NVRAM path in XML
- Wrong permissions on NVRAM
Fix:
# Verify NVRAM exists
sudo ls -lh /var/lib/libvirt/qemu/nvram/vm_VARS.fd
# Fix permissions
sudo chown libvirt-qemu:kvm /var/lib/libvirt/qemu/nvram/vm_VARS.fd
sudo chmod 600 /var/lib/libvirt/qemu/nvram/vm_VARS.fd
# Verify XML points to correct file
virsh dumpxml restored-vm | grep nvramCheck bridge/network exists:
virsh net-list --allStart default network if stopped:
virsh net-start default
virsh net-autostart defaultOr edit VM to use different network:
virsh edit restored-vmChange <source network='default'/> to an existing network.
A VM with that name already exists. Options:
-
Change the name in XML before defining:
nano vm-config.xml # Change <name>restored-vm</name> to something unique -
Undefine the conflicting VM (careful! ):
virsh undefine old-vm-name
- VM boots successfully
- Network connectivity works
- Disk size matches expectations (
qemu-img info /var/lib/libvirt/images/vm-disk.qcow2) - UEFI boot order correct (if applicable)
- Remove backup files from home directory:
rm ~/vm-disk.qcow2 ~/vm-config.xml ~/vm_VARS.fd
#!/bin/bash
set -e
# Configuration
VM_NAME="restored-vm"
DISK_IMAGE="vm-disk.qcow2"
XML_CONFIG="vm-config.xml"
NVRAM_FILE="vm_VARS.fd" # Leave empty if not using UEFI
# Move disk image
echo "Installing disk image..."
sudo mv "$DISK_IMAGE" /var/lib/libvirt/images/
sudo chown libvirt-qemu:kvm /var/lib/libvirt/images/"$DISK_IMAGE"
sudo chmod 644 /var/lib/libvirt/images/"$DISK_IMAGE"
# Move NVRAM if exists
if [ -f "$NVRAM_FILE" ]; then
echo "Installing NVRAM file..."
sudo mv "$NVRAM_FILE" /var/lib/libvirt/qemu/nvram/
sudo chown libvirt-qemu:kvm /var/lib/libvirt/qemu/nvram/"$NVRAM_FILE"
sudo chmod 600 /var/lib/libvirt/qemu/nvram/"$NVRAM_FILE"
fi
# Update XML paths (basic sed replacement - verify manually for production)
echo "Updating XML paths..."
sed -i "s|<source file='.*\. qcow2'/>|<source file='/var/lib/libvirt/images/$DISK_IMAGE'/>|" "$XML_CONFIG"
if [ -f "$NVRAM_FILE" ]; then
sed -i "s|<nvram>.*</nvram>|<nvram>/var/lib/libvirt/qemu/nvram/$NVRAM_FILE</nvram>|" "$XML_CONFIG"
fi
# Define VM
echo "Defining VM in libvirt..."
virsh define "$XML_CONFIG"
# Start VM
echo "Starting VM..."
virsh start "$VM_NAME"
echo "VM restored and started successfully!"
echo "Connect with: virt-viewer $VM_NAME"Usage:
chmod +x restore-vm.sh
./restore-vm.shTo avoid restoration issues:
-
Always backup together:
- Disk image (
.qcow2) - XML config (
virsh dumpxml) - NVRAM file (if UEFI)
- Disk image (
-
Document custom settings:
- Bridge names
- Storage pool paths
- Custom network configurations
-
Test restores periodically on a non-production system
-
Automate backups with script:
#!/bin/bash VM_NAME="your-vm" BACKUP_DIR="/backup/vms/$VM_NAME/$(date +%Y%m%d)" mkdir -p "$BACKUP_DIR" virsh dumpxml "$VM_NAME" > "$BACKUP_DIR/vm-config.xml" rsync -avP /var/lib/libvirt/images/"$VM_NAME". qcow2 "$BACKUP_DIR"/ [ -f /var/lib/libvirt/qemu/nvram/"$VM_NAME"_VARS.fd ] && \ sudo cp /var/lib/libvirt/qemu/nvram/"$VM_NAME"_VARS. fd "$BACKUP_DIR"/
| Step | Command | Purpose |
|---|---|---|
| 1 | Transfer files | Get backups to target machine |
| 2 | sudo mv vm-disk.qcow2 /var/lib/libvirt/images/ |
Install disk image |
| 3 | Edit vm-config.xml |
Fix paths for new environment |
| 4 | sudo mv vm_VARS.fd /var/lib/libvirt/qemu/nvram/ |
Restore UEFI variables |
| 5 | virsh define vm-config.xml |
Register VM with libvirt |
| 6 | virsh start restored-vm |
Boot the VM |
| 7 | virt-viewer restored-vm |
Access console |
Critical paths to verify in XML:
<source file='/var/lib/libvirt/images/vm-disk.qcow2'/><nvram>/var/lib/libvirt/qemu/nvram/vm_VARS.fd</nvram><source network='default'/>or<source bridge='br0'/>
Permissions must be correct:
# Disk image
sudo chown libvirt-qemu:kvm /var/lib/libvirt/images/vm-disk.qcow2
sudo chmod 644 /var/lib/libvirt/images/vm-disk.qcow2
# NVRAM
sudo chown libvirt-qemu:kvm /var/lib/libvirt/qemu/nvram/vm_VARS. fd
sudo chmod 600 /var/lib/libvirt/qemu/nvram/vm_VARS.fdWithout correct ownership and permissions, libvirt cannot access the files and the VM will fail to start.