1. Introduction
KVM (Kernel-based Virtual Machine) is a full virtualization solution built into the Linux kernel. Combined with QEMU (Quick Emulator), it provides a high-performance, enterprise-grade hypervisor capable of running multiple operating systems simultaneously on a single physical host.
This guide covers installation, VM creation, networking, storage, snapshots, live migration, and day-to-day management on Ubuntu 20.04/22.04/24.04 LTS.
1.1 KVM vs. Other Hypervisors
KVM/QEMU > Type-1 (bare-metal), native Linux kernel, best performance
VirtualBox > Type-2 (hosted), beginner-friendly, lower performance
VMware > Commercial Type-1/2, enterprise features, licensing costs
Hyper-V > Windows-only, tight Microsoft integration
Xen > Type-1, used by AWS, more complex setup
1.2 Architecture Overview
KVM turns the Linux kernel into a hypervisor. Each VM runs as a regular Linux process, leveraging hardware-assisted virtualization (Intel VT-x / AMD-V). QEMU handles device emulation while KVM handles CPU and memory directly.
• KVM module (/dev/kvm) — CPU & memory virtualization via hardware extensions
• QEMU — device emulation (disk, NIC, USB, display)
• libvirt — management API and daemon (libvirtd)
• virsh — command-line management tool
• virt-manager — graphical front-end
2. Prerequisites & Hardware Check
2.1 Hardware Requirements
Component Requirement
CPU Intel VT-x or AMD-V virtualization extensions (enabled in BIOS/UEFI)
RAM Minimum 4 GB host RAM; 8 GB+ recommended for running multiple VMs
Disk Sufficient free space for VM images (20–100 GB per VM typical)
Network At least one NIC; dedicated NIC recommended for bridged networking
2.2 Verify Virtualization Support
Run the following command to confirm the CPU supports hardware virtualization:
egrep -c '(vmx|svm)' /proc/cpuinfo
A result greater than 0 confirms support. Then verify KVM acceleration is available:
sudo kvm-ok
# Expected output:
# INFO: /dev/kvm exists
# KVM acceleration can be used
Note: If kvm-ok is not found, install it with: sudo apt install cpu-checker
To fully utilize KVM performance, it is essential that your processor supports hardware virtualization extensions.
To check support type:
lscpu | grep Virtualization
# Expected output:
Virtualization: VT-x or AMD-V
If you don't see any virtualization lines, or if the previous command returns nothing, your CPU may not support it, or the option may be disabled in the BIOS/UEFI.
If KVM hardware extensions are not available, you will still be able to run virtual machines, but they will be significantly slower.
2.3 Check Kernel Modules
lsmod | grep kvm
# Should show: kvm_intel (or kvm_amd) and kvm
3. Installation
3.1 Install KVM and Required Packages
sudo apt update
sudo apt install -y \
qemu-kvm \
libvirt-daemon-system \
libvirt-clients \
bridge-utils \
virt-manager \
virtinst \
ovmf
Package summary:
qemu-kvm > QEMU with KVM acceleration
libvirt-daemon-system > libvirt daemon (manages VMs)
libvirt-clients > CLI tools including virsh
bridge-utils > Network bridge utilities
virt-manager > GUI management application
virtinst > virt-install command for CLI VM creation
ovmf > UEFI firmware for VMs
3.2 Add User to Groups
sudo usermod -aG libvirt $(whoami)
sudo usermod -aG kvm $(whoami)
# Apply group changes (or log out and back in)
newgrp libvirt
3.3 Enable and Start the libvirt Service
sudo systemctl enable --now libvirtd
sudo systemctl status libvirtd
3.4 Verify Installation
virsh list --all # Should show empty VM list
virsh net-list --all # Should show 'default' network
is@ACQUARIO112-UB:~$ virsh net-list --all
Name State Autostart Persistent
--------------------------------------------
default active yes yes
If the "default network" doesn't appear, restart Host computer.
Success: If virsh list --all returns an empty table without errors, KVM is properly installed.
4. Networking
4.1 Default NAT Network
you can check the default status configuration with these commands:
virsh net-list --all
virsh net-info default
# Start and auto-start the default network
virsh net-start default
virsh net-autostart default
4.2 Network Modes Comparison
NAT > Default, VMs share host IP > Best for Outbound internet, isolated from LAN
Bridge > VMs get own LAN IP > Best for Full LAN access, servers, lab use
Isolated > No external access > Best for Secure internal lab networks
Routed > VMs routed through host > Best for Advanced setups, no bridge needed
4.3 Create a Bridged Network (br0) (only if you want to create a new custom configuration different from the default)
Bridged networking gives VMs direct access to the physical network. Edit the netplan configuration:
sudo nano /etc/netplan/01-netcfg.yaml
------------------ # Edit with these lines
network:
version: 2
ethernets:
eno1:
dhcp4: no
bridges:
br0:
interfaces: [eno1]
dhcp4: yes
parameters:
stp: false
forward-delay: 0
----------------
sudo netplan apply
4.4 Create a Custom libvirt Network (only if you want to create a new custom configuration different from the default)
sudo nano isolated-net.xml
------------------ # Edit with these lines
<network>
<name>isolated</name>
<ip address='192.168.100.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.100.2' end='192.168.100.254'/>
</dhcp>
</ip>
</network>
-------------------
virsh net-define isolated-net.xml
virsh net-start isolated
virsh net-autostart isolated
5. Storage
5.1 Storage Formats
Format Notes
qcow2 QEMU Copy-On-Write v2. Supports snapshots, thin provisioning, compression. Recommended.
raw Direct disk image. Fastest I/O, no snapshots, largest file size.
vmdk VMware format. Use for compatibility with VMware tools.
vdi VirtualBox format. Limited KVM support.
5.2 Create a Disk Image
# Create a 50 GB qcow2 image (thin-provisioned)
qemu-img create -f qcow2 /var/lib/libvirt/images/vm01.qcow2 50G
# Create a raw image
qemu-img create -f raw /var/lib/libvirt/images/vm01.raw 50G
# Check image info
qemu-img info /var/lib/libvirt/images/vm01.qcow2
5.3 Storage Pools
# List existing pools
virsh pool-list --all
# Create a directory-based pool
virsh pool-define-as mypool dir --target /data/vms
virsh pool-build mypool
virsh pool-start mypool
virsh pool-autostart mypool
# List volumes in a pool
virsh vol-list mypool
5.4 Convert Disk Images
# Convert raw to qcow2
qemu-img convert -f raw -O qcow2 vm.raw vm.qcow2
# Convert vmdk to qcow2 (import from VMware)
qemu-img convert -f vmdk -O qcow2 vm.vmdk vm.qcow2
# Compress an existing qcow2
qemu-img convert -O qcow2 -c input.qcow2 compressed.qcow2
6. Creating Virtual Machines
To install from a local ISO:
sudo virt-install \
--name ubuntu-server \
--ram 4096 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/ubuntu-server.qcow2,size=50 \
--os-variant ubuntu24.04 \
--network bridge=br0 \
--graphics vnc,listen=0.0.0.0 \
--cdrom /tmp/ubuntu-24.04.4-desktop-amd64.iso
Single command line works better:
sudo virt-install --name ubuntu-server --ram 4096 --vcpus 2 --disk path=/var/lib/libvirt/images/ubuntu-server.qcow2,size=50 --os-variant ubuntu24.04 --network bridge=default --graphics vnc,listen=0.0.0.0 --cdrom /tmp/ubuntu-24.04.4-desktop-amd64.iso
6.2 Common virt-install Options
Option Description
--name VM name (must be unique)
--ram Memory in MB (e.g., 4096 = 4 GB)
--vcpus Number of virtual CPUs
--disk Disk config: path, size (GB), format
--os-variant OS type for tuning (osinfo-query os)
--network network=NAME or bridge=BRIDGE
--graphics vnc, spice, or none
--cdrom Path to ISO for installation
--import Import existing disk image (no install)
--autostart Auto-start VM on host boot
--cpu host-passthrough Pass physical CPU features to VM
6.3 Using virt-manager (GUI)
1. Open virt-manager: Applications → System Tools → Virtual Machine Manager
2. Click File → New Virtual Machine
3. Select installation source (ISO, network, existing disk)
4. Set memory and CPU resources
5. Configure storage: create new or use existing disk
6. Set network: NAT (default) or Bridge
7. Review and click Finish
See image: Ubuntu- VM- Virtual Machine Manager
6.4 Get List of Supported OS Variants
osinfo-query os | grep -i ubuntu
# Install if needed:
sudo apt install libosinfo-bin
7. VM Management with virsh
7.1 Essential virsh Commands
Command Description
virsh list --all List all VMs (running and stopped)
virsh start <vm> Start a VM
virsh shutdown <vm> Graceful shutdown
virsh destroy <vm> Force stop (like power off)
virsh reboot <vm> Reboot a VM
virsh suspend <vm> Pause a VM
virsh resume <vm> Resume a paused VM
virsh autostart <vm> Enable auto-start on host boot
virsh autostart --disable <vm> Disable auto-start
virsh dominfo <vm> Show VM information
virsh dumpxml <vm> Show VM XML configuration
virsh edit <vm> Edit VM XML configuration
virsh undefine <vm> Remove VM definition (keeps disk)
virsh console <vm> Connect to serial console
7.2 CPU and Memory Management
# Adjust vCPUs (hot-plug if VM supports it)
virsh setvcpus vm01 4 --live --config
# Adjust memory (balloon driver required)
virsh setmem vm01 8388608 --live --config # 8 GB in KB
# Set max memory
virsh setmaxmem vm01 16777216 --config # 16 GB in KB
7.3 Connect to VM Console
# Serial console (requires console= in guest kernel cmdline)
virsh console vm01
# Exit with: Ctrl+]
# VNC viewer
virsh vncdisplay vm01 # Shows :0 or :1
vncviewer localhost:0
# SSH (once VM is running and has SSH)
ssh user@$(virsh domifaddr vm01 | grep -oP '\d+\.\d+\.\d+\.\d+')
7.4 Attach and Detach Devices
# Attach a disk
virsh attach-disk vm01 /data/extra.qcow2 vdb --subdriver qcow2 --persistent
# Detach a disk
virsh detach-disk vm01 vdb --persistent
# Attach a USB device
virsh attach-device vm01 usb-device.xml --live
# Add a NIC
virsh attach-interface vm01 network default --model virtio --persistent
8. Snapshots
8.1 Internal Snapshots (qcow2 only)
Internal snapshots are stored inside the qcow2 image. The VM must use qcow2 format.
# Create a snapshot
virsh snapshot-create-as vm01 snap1 'Before update' --disk-only
# List snapshots
virsh snapshot-list vm01
# Show snapshot details
virsh snapshot-info vm01 snap1
# Revert to a snapshot
virsh snapshot-revert vm01 snap1
# Delete a snapshot
virsh snapshot-delete vm01 snap1
8.2 External Snapshots
# Create external disk snapshot (VM can remain running)
virsh snapshot-create-as vm01 external-snap \
--diskspec vda,snapshot=external,file=/var/lib/libvirt/images/vm01-snap.qcow2 \
--disk-only --atomic
⚠️ Warning: Deleting external snapshots requires manual blockcommit/blockpull operations. Plan snapshot strategy before use in production.
8.3 Blockcommit — Merge Snapshot Back
# Commit external snapshot changes back to base image
virsh blockcommit vm01 vda --active --verbose --pivot
# Verify the active disk path
virsh domblklist vm01
9. Cloning & Templates
9.1 Clone a VM
# Shutdown the VM before cloning
virsh shutdown vm01
# Clone the VM
virt-clone \
--original vm01 \
--name vm01-clone \
--file /var/lib/libvirt/images/vm01-clone.qcow2
# Clone with auto-generated name and path
virt-clone --original vm01 --auto-clone
9.2 Create a Template VM
8. Install and configure the base OS
9. Remove machine-specific data:
# Inside the VM
sudo cloud-init clean
sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo ln -s /etc/machine-id /var/lib/dbus/machine-id
sudo history -c && sudo shutdown -h now
10. On the host, mark the VM as a template:
virsh dumpxml vm-template > template.xml
# Edit template.xml: remove MAC addresses, UUIDs
# Use virt-sysprep for automated cleanup
sudo virt-sysprep -d vm-template
11. Clone the template for each new VM
9.3 virt-sysprep — Automated Cleanup
# Install virt-sysprep
sudo apt install libguestfs-tools
# Reset VM to pristine state
sudo virt-sysprep -d vm01
# Customize: set hostname, password, SSH keys
sudo virt-customize -d vm01 \
--hostname newserver \
--root-password password:MySecret \
--ssh-inject root:file:/home/user/.ssh/id_rsa.pub
10. Live Migration
10.1 Prerequisites for Live Migration
• Both hosts must have KVM and libvirt installed
• Shared storage (NFS, GlusterFS, Ceph) for disk images OR use virsh migrate with --copy-storage-all
• SSH key-based authentication between hosts
• Same libvirt version (or compatible versions) on both hosts
• Network connectivity on the migration port (default: 49152–49215)
10.2 Configure SSH Between Hosts
# On source host — generate and copy key
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
ssh-copy-id root@destination-host
# Test
ssh root@destination-host virsh list
10.3 Perform Live Migration
# Live migration with shared storage
virsh migrate --live vm01 qemu+ssh://destination-host/system
# Live migration + copy storage (no shared storage needed)
virsh migrate --live --copy-storage-all vm01 \
qemu+ssh://destination-host/system
# Persistent migration (VM defined on destination)
virsh migrate --live --persistent vm01 \
qemu+ssh://destination-host/system
# Monitor migration progress
virsh domjobinfo vm01
ℹ️ Note: For production live migration, shared storage (NFS/Ceph) is strongly recommended. Copy-storage migration is slower and puts more load on the network.
11. Performance Tuning
11.1 CPU Tuning
# Use host-passthrough CPU model (exposes all CPU features)
virsh edit vm01
# Change: <cpu mode='custom'> → <cpu mode='host-passthrough' check='none'/>
# Pin vCPUs to physical cores (CPU pinning)
virsh vcpupin vm01 0 2 # Pin vCPU 0 to pCPU 2
virsh vcpupin vm01 1 3 # Pin vCPU 1 to pCPU 3
11.2 Memory Tuning
# Enable Huge Pages for better memory performance
echo 1024 | sudo tee /proc/sys/vm/nr_hugepages
# In VM XML — use hugepages
# <memoryBacking>
# <hugepages/>
# </memoryBacking>
# Enable KSM (Kernel Same-page Merging) for memory deduplication
echo 1 | sudo tee /sys/kernel/mm/ksm/run
11.3 Disk I/O Tuning
# Use virtio-scsi controller (best disk performance)
# In virt-install:
# --disk ...,bus=virtio
# Set disk I/O scheduler
virsh blkdeviotune vm01 vda --total-iops-sec 5000 --live
# Use native AIO for raw images
# In XML: <driver name='qemu' type='raw' cache='none' io='native'/>
11.4 Network Tuning
# Use virtio NIC (always prefer over e1000 or rtl8139)
# virt-install: --network ...,model=virtio
# Enable multiqueue virtio-net
# In VM XML:
# <driver name='vhost' queues='4'/>
# Use macvtap for near-native performance
# --network type=direct,source=eno1,source_mode=bridge,model=virtio
Optimization Description
virtio Paravirtual drivers — always use for disk/NIC/memory balloon
host-passthrough CPU mode — exposes host CPU features to guest
CPU pinning Prevents vCPU migration across pCPUs, reduces latency
Huge Pages Reduces TLB misses for memory-intensive workloads
cache=none Bypass page cache for raw disk images (use with io=native)
KSM Shares identical memory pages across VMs (good for many similar VMs)
12. Monitoring
12.1 VM Statistics
# CPU stats
virsh cpu-stats vm01 --total
# Memory stats
virsh dommemstat vm01
# Block I/O stats
virsh domblkstat vm01
# Network stats
virsh domifstat vm01 vnet0
# Overall VM info
virsh dominfo vm01
12.2 Host-Level Monitoring
# Processes per VM
ps aux | grep qemu
# Memory usage
free -h
cat /proc/meminfo | grep -i huge
# Disk usage of images
du -sh /var/lib/libvirt/images/*
qemu-img info /var/lib/libvirt/images/vm01.qcow2
# Network interfaces
ip addr show
brctl show
12.3 virt-top — Real-Time Monitoring
# Install virt-top
sudo apt install virt-top
# Run (similar to top, but for VMs)
virt-top
13. Backup & Restore
13.1 Backup Strategy
Method Notes
Cold backup (offline) Shutdown VM, copy qcow2 image. Simplest, 100% consistent.
Live snapshot backup Create snapshot, backup snapshot file, delete snapshot. VM stays online.
Agent-based backup Use qemu-guest-agent for filesystem-consistent live backups.
virsh dumpxml Always backup VM XML configuration alongside disk images.
13.2 Backup a VM (Offline)
#!/bin/bash
VM=vm01
BACKUP_DIR=/backup/vms
mkdir -p $BACKUP_DIR
# Save VM XML
virsh dumpxml $VM > $BACKUP_DIR/$VM.xml
# Shutdown VM
virsh shutdown $VM
sleep 30
# Copy disk image
cp /var/lib/libvirt/images/$VM.qcow2 $BACKUP_DIR/$VM.qcow2
# Restart VM
virsh start $VM
echo 'Backup complete: '$BACKUP_DIR
13.3 Live Backup with Snapshot
VM=vm01
SNAP_IMG=/backup/$VM-snap.qcow2
# Create external snapshot
virsh snapshot-create-as $VM livesnap \
--diskspec vda,snapshot=external,file=$SNAP_IMG \
--disk-only --atomic
# Copy base image (now frozen)
cp /var/lib/libvirt/images/$VM.qcow2 /backup/$VM-backup.qcow2
# Merge snapshot back
virsh blockcommit $VM vda --active --verbose --pivot
# Clean up snapshot file
rm -f $SNAP_IMG
virsh snapshot-delete $VM livesnap --metadata
13.4 Restore a VM
VM=vm01
BACKUP_DIR=/backup/vms
# Restore disk image
cp $BACKUP_DIR/$VM.qcow2 /var/lib/libvirt/images/$VM.qcow2
# Re-define VM from XML
virsh define $BACKUP_DIR/$VM.xml
# Start the VM
virsh start $VM
14. Troubleshooting
14.1 Common Issues
Problem Solution
libvirtd not starting Check: sudo systemctl status libvirtd Fix: sudo systemctl restart libvirtd
Permission denied on /dev/kvm Add user to kvm group: sudo usermod -aG kvm $USER
VM fails to start Check logs: sudo journalctl -xe | grep libvirt Check: virsh start vm01 (shows error)
No network in VM Verify default network: virsh net-start default Check bridge: ip addr show virbr0
VM very slow Ensure virtio drivers installed in guest Check host load: top, vmstat
Cannot connect to VNC Check firewall: sudo ufw allow 5900/tcp Verify VNC port: virsh vncdisplay vm01
Snapshot fails Disk must be qcow2 format Check disk space on host
Migration fails Verify SSH connectivity Check firewall ports 49152–49215
14.2 Useful Log Files
# libvirt daemon log
sudo journalctl -u libvirtd -f
# QEMU log for specific VM
sudo cat /var/log/libvirt/qemu/vm01.log
# Live tail
sudo tail -f /var/log/libvirt/qemu/vm01.log
14.3 Diagnostic Commands
# Check libvirt connectivity
virsh -c qemu:///system version
# Validate VM XML
virsh define vm.xml --validate
# Check host capabilities
virsh capabilities
# Check domcapabilities for a specific machine type
virsh domcapabilities --machine pc-q35-6.2
# List all virtual networks
virsh net-list --all
# Check running QEMU processes
ps aux | grep qemu | grep -v grep
15. Quick Reference Cheat Sheet
Command Action Notes
virsh list --all List VMs All states
virsh start <vm> Start VM —
virsh shutdown <vm> Graceful stop Guest OS required
virsh destroy <vm> Force stop Immediate
virsh reboot <vm> Reboot —
virsh suspend / resume <vm> Pause / unpause RAM preserved
virsh snapshot-create-as <vm> <name> Create snapshot qcow2 only
virsh snapshot-revert <vm> <name> Restore snapshot —
virsh migrate --live <vm> qemu+ssh://... Live migrate Shared storage needed
virsh dumpxml <vm> > vm.xml Export XML config For backup/restore
virsh define vm.xml Import VM from XML —
virsh undefine <vm> Remove VM config Disk preserved
virt-clone --original <vm> Clone VM Shutdown first
qemu-img info <image> Image info —
qemu-img convert -O qcow2 in out Convert image —
qemu-img resize <image> +10G Resize image Expand only
📚 Further Reading: Official docs: libvirt.org | qemu.org | Ubuntu Server Guide (ubuntu.com/server/docs/virtualization-introduction)