0%
Linux Virtualization: KVM, QEMU, and libvirt

Linux Virtualization: KVM, QEMU, and libvirt

Master Linux virtualization with KVM, QEMU, and libvirt: create VMs with virt-install, virsh, qcow2 disks, bridge networks, and cloud-init.

I

InSkillCoach

· min

Linux Virtualization: KVM, QEMU, and libvirt

KVM is the hypervisor built into the Linux kernel: it powers a large share of the world’s cloud, from OpenStack to many hosting offerings. Combined with QEMU for hardware emulation and libvirt for management, it turns any Linux machine into a complete virtualization platform, with no license fees or third-party products. In this guide, we start by checking hardware support and end with virtual machines provisioned automatically with cloud-init, covering virt-install, virsh, qcow2 disks, and network configuration along the way.

First, let’s clarify the role of each component: KVM is the kernel module that provides access to the processor’s virtualization instructions; QEMU emulates the hardware (disks, network cards, BIOS/UEFI) and relies on KVM to run guest code at near-native speed; libvirt is the management layer (API, daemon, virsh CLI) that provides a uniform way to control the whole stack.

1. Checking Hardware Support

Hardware virtualization requires the Intel VT-x or AMD-V extensions, enabled in the BIOS/UEFI.

# Compter les drapeaux de virtualisation dans /proc/cpuinfo
# vmx = Intel VT-x, svm = AMD-V ; un résultat > 0 est bon signe
grep -Ec '(vmx|svm)' /proc/cpuinfo

# Sur Ubuntu/Debian, l'outil dédié donne un verdict clair
sudo apt install cpu-checker
kvm-ok
# Résultat attendu :
# INFO: /dev/kvm exists
# KVM acceleration can be used

# Vérifier que les modules noyau sont chargés
lsmod | grep kvm
# kvm_intel (ou kvm_amd) et kvm doivent apparaître

# Vérifier le périphérique KVM
ls -l /dev/kvm

If kvm-ok reports that virtualization is disabled, reboot and enable VT-x/AMD-V in the firmware. For nested virtualization (a VM inside a VM, useful for labs):

# Vérifier le support de la virtualisation imbriquée (Intel)
cat /sys/module/kvm_intel/parameters/nested
# Y ou 1 = activé

2. Installing KVM, QEMU, and libvirt

Debian/Ubuntu

# Installer l'hyperviseur, la couche de gestion et les outils
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients \
  virtinst bridge-utils qemu-utils

# Démarrer et activer le démon libvirt
sudo systemctl enable --now libvirtd

# Ajouter votre utilisateur aux groupes nécessaires
# (déconnexion/reconnexion requise pour prise en compte)
sudo usermod -aG libvirt,kvm "$USER"

Rocky Linux/Fedora

# Le groupe de paquets virtualization installe tout le nécessaire
sudo dnf install @virtualization
sudo systemctl enable --now libvirtd
sudo usermod -aG libvirt "$USER"

Validating the Installation

# Diagnostic complet de l'hôte de virtualisation
virt-host-validate
# Toutes les lignes QEMU doivent être PASS (les WARN IOMMU sont
# acceptables si vous ne faites pas de passthrough PCI)

# Vérifier la connexion à libvirt
virsh -c qemu:///system list --all

Two connection URIs coexist: qemu:///system (VMs managed by the system daemon, recommended for servers) and qemu:///session (user VMs, unprivileged). To avoid any ambiguity:

# Forcer virsh à utiliser la connexion système par défaut
echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> ~/.bashrc

3. Creating a VM with virt-install

Preparing the Image and Storage

# Le répertoire standard des images libvirt
ls /var/lib/libvirt/images/

# Télécharger une ISO Debian (exemple)
sudo wget -P /var/lib/libvirt/images/ \
  https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.1.0-amd64-netinst.iso

Interactive Installation from an ISO

# Créer une VM : 2 vCPU, 4 Go de RAM, disque qcow2 de 20 Go
sudo virt-install \
  --name debian-test \
  --memory 4096 \
  --vcpus 2 \
  --disk path=/var/lib/libvirt/images/debian-test.qcow2,size=20,format=qcow2 \
  --cdrom /var/lib/libvirt/images/debian-13.1.0-amd64-netinst.iso \
  --os-variant debian12 \
  --network network=default \
  --graphics vnc,listen=127.0.0.1

# Lister les identifiants d'OS reconnus (optimise les pilotes virtio)
virt-install --osinfo list | grep -i debian

The installer is displayed via VNC (use virt-viewer debian-test or an SSH tunnel). For a server without a graphical interface, the cloud-init approach in section 8 is far more efficient.

4. Managing VMs with virsh

Lifecycle

# Lister les VM (actives et arrêtées)
virsh list --all

# Démarrer une VM
virsh start debian-test

# Arrêt propre (envoie un signal ACPI, l'invité doit le gérer)
virsh shutdown debian-test

# Arrêt brutal (équivalent de débrancher la prise)
virsh destroy debian-test

# Redémarrage
virsh reboot debian-test

# Démarrage automatique avec l'hôte
virsh autostart debian-test

# Console série texte (si configurée dans l'invité)
virsh console debian-test

# Supprimer une VM et ses disques définitivement
virsh undefine debian-test --remove-all-storage

Inspection and Resources

# Informations détaillées
virsh dominfo debian-test

# Adresses IP obtenues par la VM
virsh domifaddr debian-test

# Modifier la mémoire à chaud (dans la limite du maximum défini)
virsh setmem debian-test 2048M --live

# Éditer la définition XML de la VM
virsh edit debian-test

Snapshots

# Créer un snapshot avant une opération risquée
virsh snapshot-create-as debian-test avant-migration \
  --description "Avant montée de version PostgreSQL"

# Lister les snapshots
virsh snapshot-list debian-test

# Revenir à un snapshot
virsh snapshot-revert debian-test avant-migration

# Supprimer un snapshot devenu inutile
virsh snapshot-delete debian-test avant-migration

Internal snapshots require the qcow2 format. Be careful: a snapshot is not a backup. It lives on the same disk as the VM and slightly degrades performance if they accumulate.

5. qcow2 Disks with qemu-img

The qcow2 (QEMU Copy-On-Write) format supports thin provisioning (the file only grows with actual data), internal snapshots, and base images.

# Créer un disque de 50 Go (n'occupe que quelques Ko au départ)
qemu-img create -f qcow2 /var/lib/libvirt/images/data.qcow2 50G

# Inspecter une image : taille virtuelle vs taille réelle sur disque
qemu-img info /var/lib/libvirt/images/debian-test.qcow2

# Agrandir un disque (il faudra ensuite étendre la partition dans l'invité)
qemu-img resize /var/lib/libvirt/images/debian-test.qcow2 +20G

# Convertir une image VMware/VirtualBox vers qcow2
qemu-img convert -f vmdk -O qcow2 machine.vmdk machine.qcow2

# Compacter une image (VM arrêtée obligatoirement)
qemu-img convert -O qcow2 ancienne.qcow2 compactee.qcow2

Base Images and Lightweight Clones

# Créer un clone qui ne stocke que les différences avec l'image de base
qemu-img create -f qcow2 -F qcow2 \
  -b /var/lib/libvirt/images/base-debian.qcow2 \
  /var/lib/libvirt/images/clone-01.qcow2

# Vérifier la chaîne de dépendance
qemu-img info --backing-chain /var/lib/libvirt/images/clone-01.qcow2

Never modify or move a base image as long as clones depend on it: all the clones would be corrupted.

6. libvirt Networks: NAT and Bridge

The Default NAT Network

On installation, libvirt creates the default network: an internal bridge virbr0 on 192.168.122.0/24 with built-in DHCP (dnsmasq). VMs reach the Internet through NAT but are not reachable from the physical network.

# Lister les réseaux libvirt
virsh net-list --all

# Démarrer et rendre persistant le réseau par défaut
virsh net-start default
virsh net-autostart default

# Voir les baux DHCP attribués aux VM
virsh net-dhcp-leases default

Bridge: VMs as First-Class Citizens of the Local Network

For VMs to get an IP address from the physical network (like full-fledged machines), create a bridge on the host. Example with Netplan (Ubuntu Server), in /etc/netplan/01-bridge.yaml:

network:
  version: 2
  ethernets:
    enp3s0:
      dhcp4: false
  bridges:
    br0:
      interfaces: [enp3s0]
      dhcp4: true
# Appliquer avec une fenêtre de retour arrière automatique en cas de coupure
sudo netplan try

# Vérifier le pont
ip addr show br0

Then attach the VMs to the bridge:

# À la création
virt-install ... --network bridge=br0 ...

# Ou modifiez une VM existante : dans virsh edit, remplacez
# <source network='default'/> par <source bridge='br0'/>
ModeVM IP addressReachable from the LANUse case
NAT (default)192.168.122.x privateNoDev workstations, isolated labs
Bridge (br0)LAN addressYesProduction servers
IsolatedPrivate, no outbound accessNoSecurity testing, sandboxes

7. LXC Containers or Virtual Machines?

LXC runs system containers: isolated spaces that share the host’s kernel, whereas KVM boots a full kernel for each machine. The two approaches often coexist on the same host (Proxmox is a good example).

CriterionLXC (system container)KVM (virtual machine)
KernelShared with the hostSeparate for each VM
IsolationNamespaces and cgroupsHardware-based (VT-x/AMD-V)
DensityVery high (hundreds per host)Limited by dedicated RAM
StartupAbout 1 second10 to 60 seconds
Memory overheadNearly zeroRAM reserved per VM
Guest OSLinux onlyLinux, Windows, BSD
Custom kernel or modulesNot possibleYes
Multi-tenant securityAdequate, larger attack surfaceStrong, the public cloud standard
# Aperçu rapide de LXC sur Ubuntu
sudo apt install lxc
sudo lxc-create -n conteneur-test -t download -- -d debian -r bookworm -a amd64
sudo lxc-start -n conteneur-test
sudo lxc-attach -n conteneur-test

Decision rule: use LXC for many lightweight Linux environments on a single machine; use KVM whenever you need a different OS, a specific kernel, or strong isolation between tenants.

8. cloud-init: Automating Provisioning

cloud-init is the standard initialization tool for cloud images: on first boot, the VM reads a configuration (users, SSH keys, packages, commands) and configures itself. Combined with official cloud images, it completely eliminates manual installation.

Getting a Cloud Image

# Image cloud Ubuntu 24.04 (qcow2 prêt à l'emploi, environ 600 Mo)
sudo wget -P /var/lib/libvirt/images/ \
  https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

Writing the user-data Configuration

# /tmp/user-data.yaml
#cloud-config
hostname: vm-web-01
users:
  - name: admin
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... admin@poste-travail
package_update: true
packages:
  - nginx
  - htop
runcmd:
  - systemctl enable --now nginx

Creating the Provisioned VM

# Créer le disque de la VM à partir de l'image cloud (clone léger agrandi)
sudo qemu-img create -f qcow2 -F qcow2 \
  -b /var/lib/libvirt/images/noble-server-cloudimg-amd64.img \
  /var/lib/libvirt/images/vm-web-01.qcow2 20G

# virt-install génère l'ISO cloud-init automatiquement avec --cloud-init
sudo virt-install \
  --name vm-web-01 \
  --memory 2048 \
  --vcpus 2 \
  --disk path=/var/lib/libvirt/images/vm-web-01.qcow2,format=qcow2 \
  --import \
  --os-variant ubuntu24.04 \
  --network network=default \
  --cloud-init user-data=/tmp/user-data.yaml \
  --noautoconsole

# Deux minutes plus tard : récupérer l'adresse IP et se connecter
virsh domifaddr vm-web-01
ssh admin@192.168.122.53

The VM boots, creates the user, installs nginx, and enables the service with no intervention. This process can be scripted and then scaled up with Terraform (libvirt provider) or Ansible.

Best Practices

  • Run virt-host-validate after any installation or upgrade of the host.
  • Consistently work on qemu:///system for servers and set LIBVIRT_DEFAULT_URI to avoid “vanished” VMs created on the wrong connection.
  • Always specify --os-variant in virt-install: the virtio drivers it selects make a major performance difference.
  • Prefer virsh shutdown over virsh destroy: a hard stop can corrupt guest file systems.
  • Treat snapshots as temporary rollback points, never as backups: back up the qcow2 files (with the VM stopped, or via a consistent external snapshot).
  • Never touch a base image that is referenced by clones.
  • Reserve NAT for test environments; use a bridge for any service that must be reachable.
  • Standardize provisioning with cloud-init starting with your second VM: manual configuration does not scale.
  • Monitor disk space in /var/lib/libvirt/images: thin-provisioned qcow2 files grow silently until they fill the volume.

Conclusion

The KVM, QEMU, and libvirt stack delivers production-grade virtualization using only open source tools: KVM provides near-native performance, QEMU handles hardware emulation, and libvirt offers a consistent management interface from the developer workstation to the cluster. You now know how to check hardware support, install the stack, create VMs with virt-install, control them with virsh, manage qcow2 disks, choose between NAT and bridge, weigh LXC containers against virtual machines, and automate provisioning with cloud-init.

The next step is to scale up: describe your VMs in Terraform with the libvirt provider, configure them with Ansible, and your virtualization infrastructure becomes reproducible, versioned, and auditable like code.

InSkillCoach

About InSkillCoach

Training and technology expert

Coach specializing in advanced technologies and AI, backed by GNeurone Inc.

Certifications:

  • AWS Certified Solutions Architect – Professional
  • Google Cloud certifications
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
550
103

Comments

Comments are powered by GitHub Discussions

Sign in with GitHub to join the discussion

Link copied!