Run your first KVM virtual machine with libvirt and virt-install
Turn a Linux desktop or server into a KVM host: check CPU support, install libvirt, use the default NAT network, create a VM from a cloud image, take snapshots and enable autostart.
Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?
KVM is the virtualization feature built into the Linux kernel. On its own it is just a kernel module; QEMU provides the virtual hardware, libvirt manages VMs, networks and storage, and command-line tools like virsh and virt-install talk to libvirt. This tutorial is for anyone with a Linux machine who wants to run VMs without installing a full hypervisor distribution.
You will create a Debian VM from an official cloud image, which skips the whole OS installer and boots in under a minute. Commands are given for Debian/Ubuntu and for RHEL-family systems (RHEL, Rocky Linux, AlmaLinux, Fedora) where they differ.
What you will build
- A KVM host with libvirt running and the built-in
defaultNAT network active. - A VM called
vm1with 2 vCPUs, 2 GB RAM and a 20 GB disk, created with onevirt-installcommand. - SSH access into the VM using your own key, injected by cloud-init on first boot.
- A snapshot you can roll back to after breaking something on purpose.
- The VM set to start automatically when the host boots.
Before you start
- A Linux machine (physical, not itself a VM unless nested virtualization is enabled) with an x86-64 CPU that supports Intel VT-x or AMD-V. Debian 12 or newer, Ubuntu 22.04 or newer, or RHEL/Rocky/AlmaLinux 9 or newer are good choices. Red Hat’s guide asks for at least 2 GB of RAM and 6 GB of disk for the host plus the same again for each VM.
- A user with
sudorights and internet access to download packages and a 400 MB cloud image. - An SSH key pair on that machine. If
ls ~/.ssh/id_ed25519.pubshows nothing, create one withssh-keygen -t ed25519. - Time: about 45 minutes.
- Knowledge: comfortable in a terminal, installing packages with
aptordnf, and connecting withssh. Knowing the difference between a hypervisor, a VM and a disk image helps.
Step 1: Check that the CPU supports virtualization
Why: KVM needs hardware virtualization extensions. Without them, VMs either refuse to start or fall back to very slow pure emulation.
grep -Ec '(vmx|svm)' /proc/cpuinfo
lscpu | grep -i virtualization
ls -l /dev/kvm
A number greater than 0 means the CPU has Intel VT-x (vmx) or AMD-V (svm). /dev/kvm appears once the KVM kernel module is loaded, which normally happens automatically. If the count is 0 or /dev/kvm is missing, reboot into the firmware (BIOS/UEFI) setup and enable the option usually called Intel Virtualization Technology or SVM Mode.
On Ubuntu you can also install the cpu-checker package and run kvm-ok, which explains the result in plain words.
Step 2: Install QEMU, libvirt and the tools
Why: these packages give you the hypervisor, the management daemon, virsh, virt-install and qemu-img. virt-install also needs an ISO tool such as xorriso to build the small cloud-init disk used in Step 5.
Ubuntu:
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo apt update
sudo apt install qemu-kvm libvirt-daemon-system virtinst qemu-utils xorriso
Debian (use --no-install-recommends on a headless server to skip graphical extras):
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo apt update
sudo apt install --no-install-recommends qemu-system libvirt-clients libvirt-daemon-system virtinst qemu-utils xorriso
RHEL, Rocky Linux, AlmaLinux (9 or newer). These releases use modular libvirt daemons that start through systemd sockets, so the loop starts each one:
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo dnf install qemu-kvm libvirt virt-install qemu-img xorriso
for drv in qemu network nodedev nwfilter secret storage interface; do sudo systemctl start virt${drv}d{,-ro,-admin}.socket; done
sudo virt-host-validate
Check: sudo virsh version prints libvirt and QEMU versions without errors. On RHEL-family hosts, virt-host-validate should show PASS for the QEMU hardware virtualization checks; WARN lines about IOMMU only matter for passing physical devices through to VMs.
This tutorial runs virsh with sudo so that every command talks to the system-wide libvirt instance (qemu:///system). As a normal user without sudo, virsh may connect to a separate per-user session and show none of your VMs. If you prefer not to type sudo, add yourself to the libvirt group, log out and back in, and set export LIBVIRT_DEFAULT_URI=qemu:///system in ~/.bashrc.
Step 3: Start the default NAT network
Why: libvirt ships a virtual network called default. It creates a bridge named virbr0 on the host (normally 192.168.122.1/24), hands out DHCP addresses to VMs, and uses NAT so VMs can reach the internet through the host. The VMs are reachable from the host, but not from other machines on your LAN, which is a safe start.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh net-list --all
If default is listed as inactive or with autostart no (Debian does not start it automatically), start it and mark it for autostart:
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh net-start default
sudo virsh net-autostart default
ip addr show virbr0
Check: net-list shows default active yes, and ip addr show virbr0 shows the bridge address.
Step 4: Download a cloud image and prepare the disk
Why: distributions publish ready-made cloud images: small disk images with the OS already installed and cloud-init inside, which configures users and SSH keys at first boot. This is far quicker than clicking through an installer. Debian’s genericcloud image is built for VMs.
cd /tmp
curl -LO https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
curl -LO https://cloud.debian.org/images/cloud/trixie/latest/SHA512SUMS
sha512sum --ignore-missing -c SHA512SUMS
Check: the last command prints debian-13-genericcloud-amd64.qcow2: OK. Now copy the image into libvirt’s default storage directory under the VM’s name and grow it to 20 GB. The original download stays untouched, so you can reuse it for more VMs.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo cp debian-13-genericcloud-amd64.qcow2 /var/lib/libvirt/images/vm1.qcow2
sudo qemu-img resize /var/lib/libvirt/images/vm1.qcow2 20G
sudo qemu-img info /var/lib/libvirt/images/vm1.qcow2
qemu-img info should report file format: qcow2 and virtual size: 20 GiB. The disk file only uses as much real space as the data written into it. On first boot cloud-init normally grows the root partition to fill the new size.
Step 5: Create the VM with virt-install
Why: virt-install writes the VM definition for libvirt and starts it in one go. --import says the disk already contains an installed system, and --cloud-init builds a tiny configuration disk that is attached for the first boot only.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virt-install \
--name vm1 \
--memory 2048 \
--vcpus 2 \
--osinfo debian13 \
--import \
--disk path=/var/lib/libvirt/images/vm1.qcow2,bus=virtio \
--network network=default,model=virtio \
--graphics none \
--noautoconsole \
--cloud-init clouduser-ssh-key=$HOME/.ssh/id_ed25519.pub
--osinfo debian13lets libvirt pick good defaults for that OS. If your host’s OS database is older and rejects the name, list what it knows withvirt-install --osinfo list | grep -i debian, or use the genericlinux2022.bus=virtioandmodel=virtiouse paravirtualized devices, which are much faster than emulated ones.--graphics noneskips a graphical display; you will use SSH and the serial console instead.clouduser-ssh-keyinstalls your public key for the image’s default user, which isdebianon Debian cloud images (ubuntuon Ubuntu images). The$HOMEis expanded by your shell beforesudoruns, so it points at your own key.--cloud-initwas added in virt-install 3.0. Older versions report an unknown option; update the host or use a newer distribution.
Check: the command ends with a message that the domain creation completed, and the VM is running:
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh list --all
Step 6: Find the VM's address and log in
Why: the VM got its address from libvirt’s DHCP server on the default network. libvirt can tell you which lease it handed out.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh domifaddr vm1
sudo virsh net-dhcp-leases default
Give it 20 to 60 seconds after creation for the first boot and cloud-init to finish. Then connect with the address shown, for example:
ssh [email protected]
Inside the VM, check that cloud-init finished and look around:
cloud-init status --wait
hostnamectl
df -h /
ping -c 3 deb.debian.org
Check: cloud-init status reports status: done, the root filesystem is close to 20 GB, and the VM can reach the internet through NAT. If SSH does not work, attach to the serial console with sudo virsh console vm1 to see boot messages; press Ctrl+] to leave the console.
Step 7: Stop, start and inspect the VM
Why: these are the commands you will use every day. shutdown asks the guest OS to power off cleanly. destroy is the equivalent of pulling the power cable: it does not delete anything, but the guest gets no chance to save data, so use it only when shutdown fails.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh shutdown vm1
sudo virsh list --all
sudo virsh start vm1
sudo virsh dominfo vm1
Check: after shutdown, list --all shows shut off (it can take a few seconds); after start it shows running again. dominfo shows memory, vCPUs and the autostart setting.
Step 8: Take a snapshot and roll back
Why: a snapshot saves the VM’s disk state (and optionally memory) so you can return to it after a failed upgrade or experiment. With a qcow2 disk, libvirt can store the snapshot inside the same file. Taking it while the VM is shut off is the simplest and most reliable way.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh shutdown vm1
sudo virsh list --all
sudo virsh snapshot-create-as vm1 clean-install "Fresh Debian after first boot"
sudo virsh snapshot-list vm1
Now break something on purpose: start the VM, log in, and create a file that should disappear after the rollback.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh start vm1
ssh [email protected] 'touch ~/i-was-here && ls ~'
Shut the VM down again and revert. Warning: reverting is destructive; every change made since the snapshot is lost.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh shutdown vm1
sudo virsh snapshot-revert vm1 clean-install
sudo virsh start vm1
ssh [email protected] 'ls ~'
Check: the file i-was-here is gone. Snapshots in the same qcow2 file are not backups: if the disk file is lost, the snapshots go with it. When you no longer need one, remove it with sudo virsh snapshot-delete vm1 clean-install.
If snapshot creation fails with an error mentioning firmware or pflash, the VM uses UEFI firmware, which limits this simple kind of snapshot. Read the error message and the snapshot-create-as section of the virsh manual for external snapshot options.
Step 9: Start the VM automatically at boot
Why: by default VMs stay off after the host reboots. Autostart tells libvirt to start the VM when the libvirt service starts.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh autostart vm1
sudo virsh list --all --autostart
To turn it off again later, use sudo virsh autostart --disable vm1. The default network must also be set to autostart (Step 3), otherwise the VM cannot start because its network is missing.
Check that it works
- Reboot the host:
sudo reboot. - After it is back, run
sudo virsh list --all;vm1should berunningwithout you starting it. sudo virsh net-listshowsdefaultas active.ssh debian@<address from virsh domifaddr vm1>logs you in with your key and no password.sudo virsh snapshot-list vm1still listsclean-install, unless you deleted it.
If something goes wrong
virsh list --allshows nothing, although the VM exists: you are connected to the per-user session. Run the command withsudo, or usevirsh --connect qemu:///system list --all.- VM creation fails because network ‘default’ is not active: run
sudo virsh net-start defaultandsudo virsh net-autostart default. If the network does not exist at all, the libvirt network daemon or package is missing; reinstalllibvirt-daemon-system(Debian/Ubuntu) or check that thevirtnetworkdsocket is running (RHEL family). - No hardware virtualization:
virt-host-validateshows aFAILfor the hardware virtualization check, or/dev/kvmis missing. Enable VT-x/AMD-V in the firmware; inside a VM, enable nested virtualization on the outer hypervisor. - virt-install cannot create the cloud-init disk: install
xorriso(orgenisoimage), which virt-install uses to build the ISO. - SSH asks for a password or refuses the key: check that you used the right user name for the image (
debianfor Debian), and look at the serial console (sudo virsh console vm1) for cloud-init errors during boot. - Permission denied reading the disk image: keep VM disks under
/var/lib/libvirt/images. Files in your home directory may not be readable by the QEMU process, and on RHEL-family systems SELinux labels images in that directory correctly.
Clean up
Warning: the last command below deletes the VM’s disk file permanently, including all snapshots inside it. First run sudo virsh domblklist vm1 to see exactly which file will be removed, and make sure you do not need anything from the VM.
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
sudo virsh domblklist vm1
sudo virsh destroy vm1
sudo virsh undefine vm1 --snapshots-metadata --remove-all-storage
sudo virsh list --all
rm /tmp/debian-13-genericcloud-amd64.qcow2 /tmp/SHA512SUMS
destroy only stops the VM (it prints an error if the VM is already off, which is fine). undefine removes the definition, snapshot metadata and, with --remove-all-storage, the disk volume. Keep the libvirt packages installed if you want to create more VMs; the downloaded cloud image can be reused for that.
Next steps
- Install
virt-manageron a desktop for a graphical view of the same VMs, including remote hosts over SSH. - Create a Linux bridge on the host so VMs get addresses from your LAN instead of NAT.
- Write your own cloud-init
user-datafile and pass it with--cloud-init user-data=...to install packages and create users automatically. - Use
qemu-img create -f qcow2 -b base.qcow2 -F qcow2 vm2.qcow2to make thin VM disks that share one base image. - Back up VMs by copying the disk files while the VM is shut off, or explore
virsh backup-begin.
Official documentation
Did this work for you?
No votes yet. Be the first.
If it did not work, the report form below tells the editors what went wrong.
See also
- 0098 What Proxmox VE is: KVM virtual machines, LXC containers and a web UI Proxmox VE is an open source virtualization platform based on Debian. It runs KVM virtual machines and LXC containers and is managed… Beginner · Explains an idea · Linux Article Basics · Installation & first steps 15 Oct 2026
- 0100 Create a VM and a cloud-init template with qm Create KVM virtual machines from the command line with qm, then build a reusable cloud-init template from a cloud image and clone… Intermediate · Step by step · Linux Article Basics · VMs & containers 15 Oct 2026
- 0123 KVM vs QEMU vs libvirt: how Linux virtualization fits together KVM is the Linux kernel part that uses CPU virtualization extensions, QEMU is the program that emulates a full machine around it,… Beginner · Explains an idea · Linux Article Basics 15 Oct 2026
- 0124 virsh cheat sheet: everyday libvirt commands A quick reference for virsh: list, start, stop and inspect VMs, change CPUs and memory, manage networks, storage pools and snapshots, with… Intermediate · Cheat sheet · Linux Article Basics 15 Oct 2026
- 0125 Create a KVM virtual machine with virt-install Use virt-install to create libvirt/KVM guests: install from an ISO, run a text-only install over the serial console, or import a cloud… Intermediate · Step by step · Linux Article Basics 15 Oct 2026
- 0126 KVM snapshots and cloning with virsh and virt-clone Take, list, revert and delete libvirt snapshots with virsh, understand internal vs external and disk-only snapshots, and clone a stopped VM with… Advanced · Step by step · Linux Article Basics 15 Oct 2026
Repos in the wild
- proxmox/qemu-server: the Proxmox VE virtual machine manager proxmox/qemu-server Read-only GitHub mirror of qemu-server, the Proxmox VE package that manages QEMU/KVM virtual machines and provides the qm and qmrestore commands. GitHub
- libvirt: virtualization API and daemon (GitHub mirror) libvirt/libvirt libvirt is the toolkit that manages KVM/QEMU and other hypervisors through one API, plus the virsh client. The GitHub repository is a read-only mirror; development happens on GitLab. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Dominik +13,973 chars