Skip to content

Instantly share code, notes, and snippets.

@drewdomi
Created December 21, 2025 15:14
Show Gist options
  • Select an option

  • Save drewdomi/175fa26cdd5c45483f126469b0c9224d to your computer and use it in GitHub Desktop.

Select an option

Save drewdomi/175fa26cdd5c45483f126469b0c9224d to your computer and use it in GitHub Desktop.

How to Restore a Virtual Machine in virt-manager/virsh

This guide covers the complete restoration process for a KVM/QEMU virtual machine using libvirt (virt-manager/virsh) from backup files.


Prerequisites

Required backup files:

  • vm-disk. qcow2 — Virtual machine disk image
  • vm-config.xml — VM configuration exported from libvirt
  • vm_VARS.fd — UEFI NVRAM file (only if VM uses UEFI firmware)

Required tools:

# 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  # Fedora

Step 1: Transfer Backup Files to Target Machine

If 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 ~/

Step 2: Place Disk Image in Libvirt's Storage Pool

Standard location for VM disk images:

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. qcow2

Verify disk integrity (optional but recommended):

qemu-img check /var/lib/libvirt/images/vm-disk.qcow2
qemu-img info /var/lib/libvirt/images/vm-disk.qcow2

Step 3: Edit XML Configuration File

Open the XML file for editing:

nano vm-config.xml

Critical paths to verify and fix:

3.1 Disk image path

Find 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.


3.2 NVRAM path (UEFI VMs only)

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>

3.3 Network configuration

Check the <interface> section:

<interface type='network'>
  <source network='default'/>
  <model type='virtio'/>
</interface>

Options:

  • network='default' — Uses libvirt's default NAT network
  • type='bridge' + source bridge='br0' — If using bridged networking, verify bridge name exists:
ip link show | grep br

If the bridge name differs, update it in XML or switch to network='default'.


3.4 Remove machine-specific identifiers (optional)

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:

uuidgen

Step 4: Restore NVRAM File (UEFI VMs only)

Move NVRAM to libvirt's directory:

sudo 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.fd

Verify the filename matches the XML:

The filename in /var/lib/libvirt/qemu/nvram/ must match exactly what's in the <nvram> tag in your XML.


Step 5: Define (Import) the VM in libvirt

virsh define vm-config.xml

Expected output:

Domain 'restored-vm' defined from vm-config.xml

Verify the VM is registered:

virsh list --all

You should see your VM in the list with state "shut off".


Step 6: Start the Virtual Machine

Start via virsh:

virsh start restored-vm

Or open virt-manager GUI:

virt-manager
  • Find your VM in the list
  • Right-click → Open or Start

Step 7: Verify VM Operation

Check VM status:

virsh dominfo restored-vm

Access the console:

virt-viewer restored-vm

Or via virt-manager: Double-click the VM to open console.


Troubleshooting

Problem: VM fails to start with "cannot access disk"

Check permissions:

sudo ls -lZ /var/lib/libvirt/images/vm-disk.qcow2

Fix with:

sudo chown libvirt-qemu:kvm /var/lib/libvirt/images/vm-disk.qcow2
sudo chmod 644 /var/lib/libvirt/images/vm-disk. qcow2

If using SELinux (Fedora/RHEL):

sudo restorecon -R /var/lib/libvirt/images/

Problem: UEFI VM drops to firmware setup or won't boot

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 nvram

Problem: Network doesn't work

Check bridge/network exists:

virsh net-list --all

Start default network if stopped:

virsh net-start default
virsh net-autostart default

Or edit VM to use different network:

virsh edit restored-vm

Change <source network='default'/> to an existing network.


Problem: "Domain already exists" error

A VM with that name already exists. Options:

  1. Change the name in XML before defining:

    nano vm-config.xml
    # Change <name>restored-vm</name> to something unique
  2. Undefine the conflicting VM (careful! ):

    virsh undefine old-vm-name

Post-Restore Checklist

  • 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

Complete Restore Script

#!/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.sh

Backup Best Practices (for future)

To avoid restoration issues:

  1. Always backup together:

    • Disk image (.qcow2)
    • XML config (virsh dumpxml)
    • NVRAM file (if UEFI)
  2. Document custom settings:

    • Bridge names
    • Storage pool paths
    • Custom network configurations
  3. Test restores periodically on a non-production system

  4. 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"/

Summary

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.fd

Without correct ownership and permissions, libvirt cannot access the files and the VM will fail to start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment