KVM Installation and Usage Guide on Ubuntu: From Zero to Your First Virtual Machine

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

The Problem: Running Virtual Machines on a Linux Server Without a GUI

You want to test a new distro, spin up a sandbox environment to safely run risky scripts, or run multiple systems in parallel. VirtualBox is the familiar go-to — but on a headless Ubuntu server, what’s the point of installing VirtualBox? And even on a desktop, VirtualBox often breaks after kernel updates — having to run sudo /sbin/vboxconfig every time is something I got tired of fast.

I run a homelab with Proxmox VE managing 12 VMs and containers — it’s my playground for testing everything before pushing to production. Proxmox is actually built on top of KVM. So why not use KVM directly on Ubuntu when you need a lighter setup, without installing an entire separate distro?

Analysis: What Makes KVM Different from VirtualBox?

KVM (Kernel-based Virtual Machine) is a Type 1 hypervisor that runs directly on hardware through CPU virtualization extensions (Intel VT-x or AMD-V). It has been integrated into the Linux kernel since version 2.6.20 — meaning it’s available on every modern distro with nothing extra to install. VirtualBox is different: it’s a Type 2 hypervisor that operates like a regular application on the host OS, with an extra layer of overhead in between.

Three reasons I fully switched to KVM over VirtualBox for servers:

  • Near-native performance: VMs running on KVM can achieve 95–98% of bare-metal performance, especially for disk I/O and networking when using virtio drivers
  • Survives kernel updates: KVM is an official kernel module, not an external DKMS module like VirtualBox — it keeps working after kernel updates without any intervention
  • Flexible management options: virsh (CLI), virt-manager (GUI), Cockpit (web UI) — pick the right tool for your environment

Installing KVM on Ubuntu: Step by Step

Step 1: Check If Your CPU Supports Hardware Virtualization

Skip this step and you might only discover your CPU doesn’t support virtualization after everything is installed — a complete waste of time:

egrep -c '(vmx|svm)' /proc/cpuinfo

Any result greater than 0 means you’re good. If it returns 0, go into your BIOS and enable VT-x (Intel) or AMD-V. For a clearer check:

sudo apt install cpu-checker -y
kvm-ok

Expected output: KVM acceleration can be used.

Step 2: Install KVM and Related Tools

sudo apt update
sudo apt install -y \
  qemu-kvm \
  libvirt-daemon-system \
  libvirt-clients \
  bridge-utils \
  virtinst \
  virt-manager

Each package has its own role:

  • qemu-kvm: the core engine for hardware emulation and running VMs
  • libvirt-daemon-system: daemon that manages VMs via Unix socket, providing a unified API
  • libvirt-clients: CLI toolset including virsh — the main command you’ll use daily
  • bridge-utils: needed if you want VMs to get IPs directly on your LAN (bridge networking)
  • virtinst: provides virt-install and virt-clone
  • virt-manager: GUI for managing VMs (only needed if you have a desktop environment)

Step 3: Add Your User to the libvirt and kvm Groups

Skip this and every virsh command will require sudo — which gets annoying quickly:

sudo usermod -aG libvirt $USER
sudo usermod -aG kvm $USER

Then log out and back in. To apply the changes immediately without logging out:

newgrp libvirt

Step 4: Make Sure the libvirt Daemon Is Running

sudo systemctl enable --now libvirtd
systemctl status libvirtd

Status active (running) means you’re all set. On a fresh install, libvirt’s default NAT network (virbr0) doesn’t start automatically — you’ll need to enable it manually:

virsh net-list --all
virsh net-start default
virsh net-autostart default

The Best Approach: Creating and Managing VMs with virsh + virt-install

Creating Your First VM from an ISO

Download the ISO first (e.g., Ubuntu Server 22.04), then run virt-install:

virt-install \
  --name ubuntu-test \
  --ram 2048 \
  --vcpus 2 \
  --disk path=/var/lib/libvirt/images/ubuntu-test.qcow2,size=20,format=qcow2 \
  --os-variant ubuntu22.04 \
  --network network=default \
  --graphics none \
  --console pty,target_type=serial \
  --location /home/user/ubuntu-22.04-live-server-amd64.iso \
  --extra-args 'console=ttyS0,115200n8 serial'

Three parameters that often cause confusion:

  • --disk format=qcow2: thin-provisioned disk — the file on the host only occupies actual used space, not the full 20GB upfront
  • --os-variant ubuntu22.04: KVM uses this to optimize settings (IO scheduler, clock, etc.) — using the wrong OS variant can affect performance. See the full list with: osinfo-query os | grep ubuntu
  • --graphics none --console pty: serial console instead of VNC/SPICE — ideal for headless servers managed remotely over SSH

Everyday virsh Commands

# List all VMs (including those that are off)
virsh list --all

# Start / stop VM
virsh start ubuntu-test
virsh shutdown ubuntu-test    # Graceful shutdown (sends ACPI signal)
virsh destroy ubuntu-test     # Force off (like pulling the plug)

# Connect to console
virsh console ubuntu-test
# Exit console: Ctrl + ]

# Snapshot before doing something risky
virsh snapshot-create-as ubuntu-test snap-before-upgrade
virsh snapshot-revert ubuntu-test snap-before-upgrade
virsh snapshot-list ubuntu-test

# Delete VM and all its storage
virsh undefine ubuntu-test --remove-all-storage

Cloning a VM — A Significant Time Saver

Installing from scratch every time you need a new VM is extremely time-consuming. A more practical approach: prepare a “base” VM with the OS and essential packages already set up, then clone it whenever you need one. virt-clone finishes in seconds instead of spending an hour on OS installation:

# Shut down the source VM first
virsh shutdown ubuntu-base

# Clone
virt-clone \
  --original ubuntu-base \
  --name ubuntu-clone-1 \
  --auto-clone

After cloning, SSH into the new VM and update the hostname and regenerate SSH host keys:

sudo hostnamectl set-hostname ubuntu-clone-1
sudo rm /etc/ssh/ssh_host_*
sudo dpkg-reconfigure openssh-server

Monitoring Resources and Resizing Disks

# Monitor real-time CPU/RAM of all VMs
virt-top

# List available disk images
virsh vol-list --pool default

# Resize disk (VM must be shut down first)
sudo qemu-img resize /var/lib/libvirt/images/ubuntu-test.qcow2 +10G
# Then inside the VM, run growpart + resize2fs so the OS recognizes the new disk size

Common Errors and How to Fix Them

virsh Reports “permission denied” Even After Adding the Group

The most common cause: you haven’t logged out and back in after running usermod. Run groups — if libvirt doesn’t appear in the output, that’s your problem. Quick fix without logging out:

newgrp libvirt
virsh list --all

VM Created But Boots to a Black Screen

Usually caused by an incorrect --os-variant. For newer distros, older versions of libvirt may not have an entry yet — use osinfo-query os | grep ubuntu to find the exact name. If nothing matches, try --os-variant linux2022 as a fallback.

Can’t Ping the Internet from Inside the VM

The default network uses NAT — check whether the default network is actually running:

virsh net-list --all
# If "default" shows as "inactive":
virsh net-start default

Bridge networking is the next step if your VMs need their own IPs on the same LAN subnet as the host. But for lab and testing purposes, the default NAT is sufficient — simple, and requires no changes to the host’s network configuration.

Share: