Virtualization Concepts
Why This Matters
You are a sysadmin managing a rack of physical servers. Each server runs one application: a web server on one box, a database on another, a mail server on a third. Most of them sit at 5-10% CPU utilization all day, burning electricity, generating heat, and consuming rack space. When the web team needs a staging environment, they file a ticket and wait three weeks for new hardware to arrive.
Virtualization changed all of that. By running multiple virtual machines on a single physical host, organizations went from needing hundreds of physical servers to dozens. Provisioning dropped from weeks to minutes. Testing became trivial -- spin up a VM, break it, throw it away. Entire data centers shrank.
Today, virtualization is a foundational technology. Whether you are running KVM on a production hypervisor, QEMU for kernel development, or VirtualBox on your laptop for testing, understanding how virtualization works at the Linux level is essential. This chapter takes you from the concepts all the way to running your first virtual machine with open-source tools.
Try This Right Now
Check if your CPU supports hardware virtualization:
# On Intel CPUs, look for "vmx"; on AMD, look for "svm"
$ grep -E '(vmx|svm)' /proc/cpuinfo | head -1
You should see something like:
flags : ... vmx ...
If you see vmx or svm, your CPU supports hardware-assisted virtualization. If not, you may need to enable it in your BIOS/UEFI settings (look for "Intel VT-x" or "AMD-V").
Check if the KVM kernel module is loaded:
$ lsmod | grep kvm
Expected output (on Intel):
kvm_intel 368640 0
kvm 1028096 1 kvm_intel
irqbypass 16384 1 kvm
If you see kvm_intel or kvm_amd, your system is ready for KVM virtualization.
What Is Virtualization?
At its core, virtualization is the act of creating a software-based (virtual) version of something -- a computer, an operating system, a storage device, or a network. The most common form is hardware virtualization, where you run multiple virtual machines (VMs) on a single physical host, each behaving as if it were its own independent computer.
┌─────────────────────────────────────────────────────────────┐
│ Physical Hardware │
│ (CPU, RAM, Disk, Network) │
├─────────────────────────────────────────────────────────────┤
│ Hypervisor / VMM │
│ (Manages and isolates VMs) │
├──────────────┬──────────────┬───────────────────────────────┤
│ VM 1 │ VM 2 │ VM 3 │
│ ┌──────────┐ │ ┌──────────┐ │ ┌──────────┐ │
│ │ App A │ │ │ App B │ │ │ App C │ │
│ ├──────────┤ │ ├──────────┤ │ ├──────────┤ │
│ │ Libs │ │ │ Libs │ │ │ Libs │ │
│ ├──────────┤ │ ├──────────┤ │ ├──────────┤ │
│ │ Guest OS │ │ │ Guest OS │ │ │ Guest OS │ │
│ │ (Ubuntu) │ │ │ (CentOS) │ │ │ (Debian) │ │
│ └──────────┘ │ └──────────┘ │ └──────────┘ │
└──────────────┴──────────────┴───────────────────────────────┘
Each VM gets its own:
- Virtual CPUs (vCPUs)
- Virtual RAM
- Virtual disk (a file on the host, typically)
- Virtual network interface
- Its own kernel, running its own operating system
The hypervisor (also called a Virtual Machine Monitor or VMM) is the software that creates and manages these virtual machines.
The Two Types of Hypervisors
Not all hypervisors are created equal. The industry classifies them into two types based on where they sit in the software stack.
Type 1: Bare-Metal Hypervisors
A Type 1 hypervisor runs directly on the physical hardware, with no host operating system underneath. The hypervisor is the operating system (or is deeply integrated into it).
┌───────────────────────────────────┐
│ VM 1 │ VM 2 │ VM 3 │
├───────────┴───────────┴──────────┤
│ Type 1 Hypervisor │
│ (runs directly on hardware) │
├──────────────────────────────────┤
│ Physical Hardware │
└──────────────────────────────────┘
Open-source examples:
- KVM (Kernel-based Virtual Machine) -- built into the Linux kernel itself
- Xen -- used by AWS for years, powers many cloud providers
KVM is technically interesting because the Linux kernel becomes the hypervisor. When you load the KVM module, your Linux host becomes a Type 1 hypervisor that also happens to run a general-purpose operating system.
Type 2: Hosted Hypervisors
A Type 2 hypervisor runs as an application on top of a conventional operating system, just like any other program.
┌───────────────────────────────────┐
│ VM 1 │ VM 2 │ VM 3 │
├───────────┴───────────┴──────────┤
│ Type 2 Hypervisor │
│ (runs as application) │
├──────────────────────────────────┤
│ Host Operating System │
│ (Linux, Windows, macOS) │
├──────────────────────────────────┤
│ Physical Hardware │
└──────────────────────────────────┘
Open-source examples:
- QEMU (Quick EMUlator) -- can emulate entirely in software
- VirtualBox -- popular desktop virtualization tool
Think About It: KVM is often called a Type 1 hypervisor even though it runs within Linux. Why? Because once the KVM module is loaded, the Linux kernel itself is the hypervisor -- it runs directly on hardware and manages VMs at the kernel level. The distinction between "bare metal" and "hosted" gets blurry with KVM, and that is actually one of its strengths.
Full Virtualization vs Paravirtualization
There are two fundamental approaches to virtualizing hardware.
Full Virtualization
In full virtualization, the guest operating system runs completely unmodified. It believes it is running on real hardware. The hypervisor traps privileged instructions from the guest and emulates them.
Pros:
- Run any OS without modification (Windows, BSD, etc.)
- Guests need zero awareness of virtualization
Cons:
- Historically slower due to trap-and-emulate overhead
- Hardware-assisted virtualization (VT-x/AMD-V) has largely eliminated the performance gap
KVM with hardware-assisted virtualization is full virtualization -- your guest OS does not need to know it is virtualized.
Paravirtualization
In paravirtualization, the guest OS is modified to be aware it is running in a virtual environment. Instead of executing privileged instructions that need to be trapped, the guest makes explicit "hypercalls" to the hypervisor.
Pros:
- Can be faster for certain I/O-heavy operations
- Efficient communication with the hypervisor
Cons:
- Requires a modified guest kernel
- Cannot run unmodified proprietary operating systems
Xen pioneered paravirtualization. Today, even full-virtualization setups use paravirtual drivers (like virtio in KVM) for disk and network I/O because they are faster than emulating real hardware.
Full Virtualization: Paravirtualization:
Guest OS (unmodified) Guest OS (modified)
│ │
│ privileged instruction │ hypercall
│ │
▼ ▼
Hypervisor traps & emulates Hypervisor handles directly
│ │
▼ ▼
Hardware Hardware
KVM and QEMU: The Linux Virtualization Stack
KVM and QEMU are the two workhorses of Linux virtualization. They are separate projects that work together beautifully.
KVM (Kernel-based Virtual Machine)
KVM is a Linux kernel module that turns the kernel into a hypervisor. It leverages hardware virtualization extensions (Intel VT-x or AMD-V) to run guest code directly on the CPU at near-native speed.
What KVM handles:
- CPU virtualization (using hardware VT-x/AMD-V)
- Memory virtualization (using hardware EPT/NPT)
- Interrupt handling
What KVM does not handle:
- Device emulation (disks, network cards, displays)
- VM management (creating, configuring VMs)
That is where QEMU comes in.
QEMU (Quick EMUlator)
QEMU is a user-space emulator that can emulate an entire computer system -- CPU, memory, devices, everything -- purely in software. It can run a guest OS for a completely different CPU architecture (e.g., run ARM code on x86).
When paired with KVM, QEMU delegates CPU and memory virtualization to KVM (which uses hardware acceleration) and handles everything else: disk controllers, network cards, USB devices, display output.
┌───────────────────────────────────────────┐
│ User Space │
│ ┌─────────────────────────────────┐ │
│ │ QEMU Process │ │
│ │ ┌────────┐ ┌──────────────┐ │ │
│ │ │ Device │ │ VM Config │ │ │
│ │ │ Emulat.│ │ & Management │ │ │
│ │ └────────┘ └──────────────┘ │ │
│ └──────────────┬──────────────────┘ │
│ │ /dev/kvm │
├──────────────────┼────────────────────────┤
│ Kernel Space │ │
│ ┌──────────────▼──────────────────┐ │
│ │ KVM Module │ │
│ │ (CPU & Memory Virtualization) │ │
│ └─────────────────────────────────┘ │
├───────────────────────────────────────────┤
│ Hardware (VT-x / AMD-V) │
└───────────────────────────────────────────┘
Hands-On: Running a VM with QEMU/KVM
Let us install the tools and run a minimal VM.
Install on Debian/Ubuntu:
$ sudo apt update
$ sudo apt install -y qemu-system-x86 qemu-utils libvirt-daemon-system \
libvirt-clients virtinst virt-manager
Install on Fedora/RHEL:
$ sudo dnf install -y qemu-kvm libvirt virt-install virt-manager
Distro Note: On Arch Linux, install
qemu-full,libvirt,virt-manager, anddnsmasq. Then enable and startlibvirtd.service.
Download a small test image (Alpine Linux, ~60MB):
$ mkdir -p ~/vms && cd ~/vms
$ wget https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/x86_64/alpine-virt-3.19.1-x86_64.iso
Launch a VM directly with QEMU (without libvirt):
# Create a 2GB virtual disk
$ qemu-img create -f qcow2 alpine-test.qcow2 2G
# Boot the VM with KVM acceleration
$ qemu-system-x86_64 \
-enable-kvm \
-m 512 \
-cpu host \
-smp 2 \
-drive file=alpine-test.qcow2,format=qcow2 \
-cdrom alpine-virt-3.19.1-x86_64.iso \
-boot d \
-net nic -net user \
-nographic
Let us break down those flags:
| Flag | Meaning |
|---|---|
-enable-kvm | Use KVM hardware acceleration |
-m 512 | 512 MB of RAM |
-cpu host | Pass through the host CPU model |
-smp 2 | 2 virtual CPUs |
-drive file=...,format=qcow2 | Virtual hard disk |
-cdrom ... | ISO image as virtual CD-ROM |
-boot d | Boot from CD-ROM first |
-net nic -net user | User-mode networking (NAT) |
-nographic | Console output in terminal |
Press Ctrl+A then X to exit the QEMU console.
The libvirt Ecosystem
Running QEMU directly with command-line flags works, but it does not scale. Managing dozens of VMs with raw QEMU commands would be a nightmare. This is where libvirt comes in.
libvirt is a toolkit that provides a unified API for managing virtualization platforms. It supports KVM/QEMU, Xen, LXC, VirtualBox, and more. Think of it as an abstraction layer.
┌───────────────────────────────────────────┐
│ Management Tools │
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ │
│ │ virsh │ │virt- │ │ Cockpit / │ │
│ │ (CLI) │ │manager │ │ oVirt │ │
│ │ │ │(GUI) │ │ (Web) │ │
│ └────┬────┘ └────┬─────┘ └─────┬─────┘ │
│ └───────────┼──────────────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ libvirtd │ │
│ │ (daemon) │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────┐ ┌────────┐ ┌────────┐ │
│ │ QEMU/ │ │ Xen │ │ LXC │ │
│ │ KVM │ │ │ │ │ │
│ └───────┘ └────────┘ └────────┘ │
└───────────────────────────────────────────┘
Key components:
- libvirtd -- the daemon that manages VMs
- virsh -- the command-line interface
- virt-manager -- a graphical desktop application
- virt-install -- command-line tool for creating new VMs
Hands-On: Managing VMs with virsh
Start the libvirt daemon:
$ sudo systemctl enable --now libvirtd
List all VMs (called "domains" in libvirt terminology):
$ sudo virsh list --all
Id Name State
-----------------------
Create a VM using virt-install:
$ sudo virt-install \
--name alpine-test \
--ram 512 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/alpine-test.qcow2,size=2 \
--cdrom ~/vms/alpine-virt-3.19.1-x86_64.iso \
--os-variant alpinelinux3.19 \
--network network=default \
--graphics none \
--console pty,target_type=serial
Distro Note: If
--os-variant alpinelinux3.19is not recognized, runosinfo-query os | grep alpineto find valid values, or use--os-variant generic.
Common virsh commands:
# List running VMs
$ sudo virsh list
# Start a VM
$ sudo virsh start alpine-test
# Graceful shutdown
$ sudo virsh shutdown alpine-test
# Force power off (like pulling the plug)
$ sudo virsh destroy alpine-test
# Connect to VM console
$ sudo virsh console alpine-test
# View VM details
$ sudo virsh dominfo alpine-test
# View VM's XML configuration
$ sudo virsh dumpxml alpine-test
# Suspend (pause) a VM
$ sudo virsh suspend alpine-test
# Resume a paused VM
$ sudo virsh resume alpine-test
# Take a snapshot
$ sudo virsh snapshot-create-as alpine-test snap1 "First snapshot"
# List snapshots
$ sudo virsh snapshot-list alpine-test
# Revert to a snapshot
$ sudo virsh snapshot-revert alpine-test snap1
# Delete a VM (keeps disk by default)
$ sudo virsh undefine alpine-test
# Delete a VM and its disk
$ sudo virsh undefine alpine-test --remove-all-storage
Safety Warning:
virsh destroydoes NOT delete the VM. It force-powers it off (equivalent to pulling the power cord).virsh undefine --remove-all-storagepermanently deletes the VM and its disk image.
virt-manager: The Graphical Interface
If you are on a desktop with X11/Wayland, virt-manager provides a full GUI for creating and managing VMs:
$ virt-manager
It connects to libvirtd and shows all your VMs. You can:
- Create new VMs with a wizard
- View VM consoles (graphical or serial)
- Adjust CPU, memory, and disk settings
- Manage virtual networks and storage pools
- Take and manage snapshots
For headless servers, virt-manager can connect to remote libvirtd instances over SSH:
$ virt-manager --connect qemu+ssh://user@remote-host/system
Containers vs Virtual Machines
This is one of the most important architectural distinctions in modern infrastructure. Let us compare them clearly.
Virtual Machines: Containers:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ App A │ │ App B │ │ App A │ │ App B │
├──────────┤ ├──────────┤ ├──────────┤ ├──────────┤
│ Libs │ │ Libs │ │ Libs │ │ Libs │
├──────────┤ ├──────────┤ └────┬─────┘ └────┬─────┘
│ Guest OS │ │ Guest OS │ │ │
│ (kernel) │ │ (kernel) │ ─────┴────────────┴─────
└────┬─────┘ └────┬─────┘ │ Host OS Kernel │
─────┴────────────┴───── ├───────────────────────┤
│ Hypervisor │ │ Hardware │
├──────────────────────┤ └───────────────────────┘
│ Hardware │
└──────────────────────┘
| Feature | Virtual Machines | Containers |
|---|---|---|
| Isolation | Full hardware-level | Process-level (namespaces, cgroups) |
| Kernel | Each VM has its own | Shares the host kernel |
| Boot time | Seconds to minutes | Milliseconds to seconds |
| Resource overhead | Hundreds of MB per VM | A few MB per container |
| Disk footprint | GBs per VM image | MBs per container image |
| Security boundary | Strong (separate kernel) | Weaker (shared kernel attack surface) |
| OS flexibility | Run any OS (Windows, BSD) | Linux guests on Linux host only |
| Density | Tens per host | Hundreds to thousands per host |
| Use case | Multi-OS, strong isolation | Microservices, CI/CD, app packaging |
When to Use VMs
- You need to run different operating systems (Windows on a Linux host)
- You require strong security isolation (multi-tenant environments)
- You are running untrusted workloads
- You need different kernel versions for different applications
- Your application requires kernel modules not available on the host
When to Use Containers
- You want fast startup times and high density
- You are deploying microservices
- You need consistent development/production environments
- You want lightweight, portable packaging of applications
- Your CI/CD pipeline needs to spin up environments rapidly
Think About It: Many production environments use both. VMs provide the security boundary between tenants, and containers run inside those VMs for application packaging. Cloud providers like AWS run your containers inside micro-VMs (Firecracker) for exactly this reason.
Debug This
A colleague complains their VM will not start. Here is the error:
$ sudo virsh start myvm
error: Failed to start domain 'myvm'
error: internal error: Cannot find suitable emulator for x86_64
What is wrong?
The QEMU binary is not installed or not in the expected path. libvirt cannot find qemu-system-x86_64.
Fix:
# Install QEMU
$ sudo apt install qemu-system-x86 # Debian/Ubuntu
$ sudo dnf install qemu-kvm # Fedora/RHEL
# Verify it is installed
$ which qemu-system-x86_64
/usr/bin/qemu-system-x86_64
# Restart libvirtd
$ sudo systemctl restart libvirtd
# Try again
$ sudo virsh start myvm
Another common issue -- KVM acceleration not available:
$ sudo virsh start myvm
error: internal error: process exited while connecting to monitor:
Could not access KVM kernel module: No such file or directory
Fix: Load the KVM module:
$ sudo modprobe kvm_intel # Intel CPUs
$ sudo modprobe kvm_amd # AMD CPUs
# Verify
$ lsmod | grep kvm
If modprobe fails, check your BIOS settings -- hardware virtualization (VT-x or AMD-V) may be disabled.
Virtual Networking
libvirt sets up a default virtual network using a bridge called virbr0. VMs connect to this bridge and can reach the internet through NAT on the host.
# View virtual networks
$ sudo virsh net-list --all
Name State Autostart Persistent
--------------------------------------------
default active yes yes
# View network details
$ sudo virsh net-info default
Name: default
UUID: a1b2c3d4-...
Active: yes
Persistent: yes
Autostart: yes
Bridge: virbr0
# Check the bridge on the host
$ ip addr show virbr0
5: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
VMs on this network get IPs in the 192.168.122.0/24 range via DHCP served by dnsmasq (which libvirt manages automatically).
Virtual Disk Formats
QEMU supports several disk image formats:
| Format | Description |
|---|---|
| qcow2 | QEMU Copy-On-Write v2. Supports snapshots, compression, encryption. The standard choice. |
| raw | No overhead, slightly faster I/O, but no snapshots or compression. |
| vmdk | VMware format. Useful for compatibility. |
| vdi | VirtualBox format. |
Working with qcow2 images:
# Create a 20GB thin-provisioned image (only uses space as data is written)
$ qemu-img create -f qcow2 disk.qcow2 20G
# Check image details
$ qemu-img info disk.qcow2
# Convert between formats
$ qemu-img convert -f raw -O qcow2 disk.raw disk.qcow2
# Resize a disk image
$ qemu-img resize disk.qcow2 +10G
What Just Happened?
┌─────────────────────────────────────────────────────────────┐
│ CHAPTER RECAP │
├─────────────────────────────────────────────────────────────┤
│ │
│ Virtualization runs multiple OS instances on one machine. │
│ │
│ Type 1 hypervisors (KVM, Xen) run on bare metal. │
│ Type 2 hypervisors (VirtualBox, QEMU) run as apps. │
│ │
│ KVM is a kernel module; QEMU handles device emulation. │
│ Together they form the standard Linux virtualization │
│ stack. │
│ │
│ libvirt (virsh, virt-manager) provides unified VM │
│ management across hypervisor backends. │
│ │
│ Full virtualization = unmodified guest OS. │
│ Paravirtualization = modified guest with hypercalls. │
│ virtio = paravirtual drivers for best I/O performance. │
│ │
│ VMs provide strong isolation with full kernels. │
│ Containers are lightweight but share the host kernel. │
│ Production environments often use both. │
│ │
└─────────────────────────────────────────────────────────────┘
Try This
-
Basic VM creation: Install QEMU/KVM and libvirt on your system. Download the Alpine Linux ISO and create a VM using
virt-install. Boot it, log in, and rununame -ainside the VM. How does the kernel differ from your host? -
Snapshot practice: Create a snapshot of your running VM. Make a change inside the VM (create a file, install a package). Revert to the snapshot and verify the change is gone.
-
Explore virsh: Use
virsh dominfo,virsh domstats, andvirsh dumpxmlto inspect your VM. Find out how much memory is allocated and how many vCPUs it has. -
Disk management: Create a second qcow2 disk image and attach it to your running VM using
virsh attach-disk. Inside the VM, partition, format, and mount it. -
Bonus Challenge: Set up a second VM on the same virtual network. From VM 1, ping VM 2 by IP address. This demonstrates that libvirt's virtual network provides layer-2 connectivity between guests.