How to Install KVM on Ubuntu 26.04, 24.04 and 22.04

Install KVM on Ubuntu 26.04, 24.04, and 22.04 with one explicit QEMU provider that works across all three releases. Configure system-wide libvirt for predictable storage and private NAT networking, then create virtual machines without an unnoticed fallback to software emulation.

PublishedAuthorJoshua JamesRead time12 minGuide typeUbuntu

On Ubuntu, KVM is the Linux kernel’s hardware-acceleration layer rather than a standalone virtual machine application. A working host needs CPU virtualisation exposed to Ubuntu, QEMU to run guests, and libvirt to manage guest storage, networks, and lifecycle.

The steps support Ubuntu 26.04, 24.04, and 22.04 on Ubuntu Desktop or Server with an amd64 (x86_64) host. Stock Ubuntu kernels already include KVM, so the baseline does not need a PPA, DKMS module, or separate kernel-header installation. The main path uses system-wide libvirt, while graphical management, virtual TPM support, and a physical network bridge remain optional.

Install KVM on Ubuntu Linux

Review Ubuntu Updates Before Installation

Refresh the APT package index, then review wider Ubuntu updates before changing the virtualisation stack:

sudo apt update
apt list --upgradable

A full upgrade affects the wider system, not only the KVM packages, and is not required to install KVM. Review the transaction before accepting it, and expect kernel or core-library updates to require a reboot:

sudo apt upgrade

If the upgrade installs a new kernel, reboot before the final KVM verification so /dev/kvm and the active kernel modules represent the updated host.

Check Hardware Virtualisation Support

Confirm that Ubuntu reports the x86 architecture required by this package set:

dpkg --print-architecture
uname -m

Expected output:

amd64
x86_64

Continue when the commands report amd64 and x86_64. Stop if either value differs. ARM64 hosts require different QEMU and firmware packages, so these install commands do not apply to them.

Install Ubuntu’s hardware capability checker, then test whether KVM acceleration is available:

sudo apt install cpu-checker
sudo kvm-ok

These commands use sudo for system package changes and the most complete hardware check. If the account cannot use sudo, add a user to sudoers on Ubuntu before continuing.

Continue only when kvm-ok confirms that KVM acceleration can be used. A failure usually means Intel VT-x/VMX or AMD-V/SVM is disabled in firmware, unavailable on the processor, or not exposed to an Ubuntu guest running inside another hypervisor. QEMU can emulate a processor without KVM, but the performance is substantially lower and is outside this accelerated-host setup.

Install QEMU and libvirt

The Ubuntu Server libvirt documentation describes the core daemon and access model. Ubuntu exposes qemu-kvm as a virtual package without its own concrete candidate on all three supported releases. Ubuntu 24.04 and 22.04 currently resolve that name to qemu-system-x86, but Ubuntu 26.04 offers standard and HWE providers and rejects the ambiguous virtual name. Select the standard x86 provider explicitly for one command that behaves consistently on all three Ubuntu releases:

sudo apt install qemu-system-x86 qemu-utils libvirt-daemon-system libvirt-clients
PackageRoleWhy It Is Included
qemu-system-x86x86 QEMU system emulatorProvides the x86 system binary used with the host’s KVM acceleration.
qemu-utilsVirtual disk utilitiesProvides tools such as qemu-img for QCOW2 and other virtual disk formats.
libvirt-daemon-systemSystem-wide libvirt deploymentProvides the daemon configuration, QEMU driver integration, default network components, and system resource access needed for system-wide guest management.
libvirt-clientsCommand-line management toolsProvides tools such as virsh and virt-host-validate for verification and guest management.

The default libvirt network does not require bridge-utils. It gives guests outbound connectivity through NAT; a physical bridge is a separate Netplan and network-design task for guests that must appear directly on the LAN.

Install Optional KVM Management Packages

Choose only the tools required by the host. A server managed over SSH does not need the full graphical interface. The virt-manager, virt-viewer, and virtinst packages come from Ubuntu’s Universe component on all three supported releases; the core KVM and libvirt packages above come from Main. If a minimal installation reports no candidate for an optional package, enable Universe before retrying.

PackageUse It ForHost Fit
virt-managerCreating and managing guests through a graphical interface; it also installs the virt-install commandUbuntu Desktop, a workstation, or a non-production test host with a GUI
virtinstCreating guests with virt-install from the command lineUbuntu Server, automation, and repeatable CLI workflows
virt-viewerOpening a guest console without the full managerLocal or remote graphical administration
qemu-system-guiQEMU graphical display and audio modulesMinimal hosts where recommended packages are disabled but SPICE or VNC is required
ovmfUEFI firmware for x86 guestsamd64 hosts that will create UEFI guests
swtpm and swtpm-toolsSoftware-backed virtual TPM devicesGuests that require or benefit from a TPM

For a local graphical manager, install virt-manager. Install virt-viewer as well when a lightweight standalone console is useful:

sudo apt install virt-manager virt-viewer

For command-line guest creation on a desktop or server, install virtinst. This package name works across the supported Ubuntu releases and provides the virt-install command:

sudo apt install virtinst

APT normally installs qemu-system-gui and ovmf as recommended dependencies of qemu-system-x86. Install them explicitly when recommended packages were disabled or when the host must provide the SPICE display and UEFI firmware required by the UEFI guest command:

sudo apt install qemu-system-gui ovmf

Add software-backed TPM support only for guests that require a virtual TPM:

sudo apt install swtpm swtpm-tools

Grant a User Access to System libvirt

The trusted account may already belong to the libvirt group after installation. Check its current groups before changing membership:

id -nG "$USER" | tr ' ' '\n' | grep -x libvirt

If the command prints nothing, add the account explicitly:

sudo adduser "$USER" libvirt

Log out of the desktop session and sign back in, or disconnect and reconnect the SSH session. Group changes do not apply fully to the existing login session. Confirm that the new session includes libvirt:

id -nG | tr ' ' '\n' | grep -x libvirt

Do not add every user to this group. System libvirt can manage host storage, networks, and virtual machines. Directly launching QEMU outside libvirt may also require kvm group access, but that separate workflow is not needed for the system-libvirt path used here.

Verify KVM and libvirt on Ubuntu

Validate KVM Acceleration

Run the hardware check again after any package update or reboot, then inspect the KVM device:

sudo kvm-ok
stat -c '%A %U %G %n' /dev/kvm

A usable host has /dev/kvm, and kvm-ok confirms hardware acceleration. The device is normally owned by the kvm group; libvirt opens it through its managed QEMU processes, so ordinary system-libvirt administration still uses the libvirt group.

Validate the QEMU Host

virt-host-validate checks the host features required or commonly used by the QEMU driver:

sudo virt-host-validate qemu

The KVM device and core QEMU checks should pass. Warnings for optional facilities such as an IOMMU, secure guests, or another advanced feature are not automatically fatal; resolve them only when the planned virtual machine needs that capability.

Connect to System libvirt

Use an explicit system libvirt connection URI for predictable storage and network behaviour:

virsh --connect qemu:///system list --all

A successful command may show no virtual machines on a new host. The important result is that virsh connects without an authentication, socket, or permission error.

ConnectionResource ScopeUse CaseImportant Limitation
qemu:///systemSystem-wide guests, storage, and networksNormal administrator-managed KVM hostRequires authorised access through the libvirt group or policy controls
qemu:///sessionGuests owned by one logged-in userIsolated desktop experiments using files in that user’s home directoryUses restricted per-user networking and does not share the system guest inventory

Do not create some guests under qemu:///session and others under qemu:///system without a deliberate reason. The two connections have separate guest inventories, storage access, and networking, which commonly makes a working VM appear to have disappeared.

Configure the Default KVM Network

The standard libvirt installation includes a private default network. Guests receive addresses from libvirt and reach external networks through NAT, so ordinary outbound guest access does not require an inbound UFW port on the host.

List every defined network, including inactive networks:

virsh --connect qemu:///system net-list --all

If default is listed but inactive, enable it for future boots and start it now:

virsh --connect qemu:///system net-autostart default
virsh --connect qemu:///system net-start default

Recheck the state:

virsh --connect qemu:///system net-list --all

Use a physical bridge only when guests must obtain LAN addresses or accept direct connections from other machines without port forwarding. That change modifies the host’s real network configuration and should be planned separately, especially on a remote server where an incorrect Netplan change can disconnect SSH.

Create a KVM Virtual Machine on Ubuntu

Create a Guest with virt-manager

Launch the graphical manager from an Ubuntu desktop session:

virt-manager --connect qemu:///system

With the system QEMU/KVM connection open, select the new virtual machine button, choose an installation ISO, then assign memory, virtual CPUs, storage, and the default virtual network. Keep the guest architecture aligned with the host when you want KVM acceleration. Select UEFI firmware or add a virtual TPM only when the guest requires those devices and the matching optional packages are installed.

Use installation media from a trusted source and verify its published checksum before attaching it to a guest. If an ISO stored in a home directory triggers a libvirt or AppArmor access error, copy it into libvirt’s standard image directory before selecting it. Enter the real source path in the terminal that will copy the file:

read -r -e -p 'Full path to the installer ISO: ' ISO_SOURCE

Copy the ISO only when the source is readable and the destination does not already exist. If this command prints an error, correct the path or choose a different filename before rerunning it:

ISO_DEST="/var/lib/libvirt/images/$(basename -- "$ISO_SOURCE")"

if [ ! -f "$ISO_SOURCE" ] || [ ! -r "$ISO_SOURCE" ]; then
  printf 'The ISO is not a readable file: %s\n' "$ISO_SOURCE" >&2
elif [ -e "$ISO_DEST" ]; then
  printf 'The destination already exists: %s\n' "$ISO_DEST" >&2
elif ! sudo install -m 0644 "$ISO_SOURCE" "$ISO_DEST"; then
  printf 'Could not copy the ISO to %s. Check permissions and free space.\n' "$ISO_DEST" >&2
else
  printf 'Copied ISO to %s\n' "$ISO_DEST"
fi

Create a Guest with virt-install

Create a UEFI VM named ubuntu-guest with 4 GiB of memory, two virtual CPUs, a 30 GiB QCOW2 disk, the default NAT network, a virtio display, and a SPICE console. Use x86 installation media on the amd64 host. First, enter the verified ISO path in the terminal that will create the guest:

read -r -e -p 'Full path to the installer ISO: ' ISO_SOURCE

This creation command requires ovmf, refuses an existing guest or storage path, and copies the ISO into the standard libvirt image directory before defining the guest. If a check prints an error, resolve it before rerunning the command:

VM_NAME='ubuntu-guest'
ISO_PATH="/var/lib/libvirt/images/${VM_NAME}-installer.iso"
DISK_PATH="/var/lib/libvirt/images/${VM_NAME}.qcow2"

if ! command -v virt-install >/dev/null 2>&1; then
  printf 'Install the virtinst package before creating the guest.\n' >&2
elif [ "$(dpkg --print-architecture)" != 'amd64' ]; then
  printf 'This example requires an amd64 KVM host.\n' >&2
elif [ ! -f "$ISO_SOURCE" ] || [ ! -r "$ISO_SOURCE" ]; then
  printf 'The ISO is not a readable file: %s\n' "$ISO_SOURCE" >&2
elif ! dpkg-query -W -f='${db:Status-Abbrev}' ovmf 2>/dev/null | grep -q '^ii'; then
  printf 'Install ovmf before creating this UEFI guest.\n' >&2
elif virsh --connect qemu:///system dominfo "$VM_NAME" >/dev/null 2>&1; then
  printf 'A libvirt guest named %s already exists.\n' "$VM_NAME" >&2
elif [ -e "$ISO_PATH" ] || [ -e "$DISK_PATH" ]; then
  printf 'Choose another VM name or move the existing %s files first.\n' "$VM_NAME" >&2
elif ! sudo install -m 0644 "$ISO_SOURCE" "$ISO_PATH"; then
  printf 'Could not copy the ISO to %s. Check permissions and free space.\n' "$ISO_PATH" >&2
else
  virt-install \
    --connect qemu:///system \
    --virt-type kvm \
    --name "$VM_NAME" \
    --memory 4096 \
    --vcpus 2 \
    --disk "path=${DISK_PATH},size=30,format=qcow2,bus=virtio" \
    --cdrom "$ISO_PATH" \
    --osinfo detect=on,require=on \
    --boot uefi \
    --network network=default,model=virtio \
    --graphics spice \
    --video virtio \
    --noautoconsole
fi

Adjust the fixed VM_NAME, memory, CPU, and disk values before running the block when the guest needs different resources. The --virt-type kvm option prevents an unnoticed fallback to software emulation, while --boot uefi lets libvirt select the matching firmware from Ubuntu’s packaged capabilities instead of hard-coding a release-specific firmware path. The --osinfo check requires virt-install to identify the installation media so it can choose suitable guest defaults. If detection fails, run virt-install --osinfo list and supply the correct listed short ID instead of silently falling back to a generic guest profile. If creation fails after the ISO copy, inspect the named guest and both exact storage paths before removing or overwriting anything. The command requests SPICE without setting a public listen address; keep remote console access behind an SSH-backed libvirt connection instead of changing the listener to 0.0.0.0.

The --noautoconsole option returns control to the terminal while the installer runs. When an ISO-based installation finishes, the guest can stop instead of rebooting automatically; check its state with virsh --connect qemu:///system list --all and start it after installation when required.

On the same Ubuntu desktop, open the new console with:

virt-viewer --connect qemu:///system ubuntu-guest

For a headless KVM host, install virt-manager or virt-viewer on an Ubuntu workstation and connect over SSH with keys. Configure SSH access on Ubuntu first, and confirm the remote SSH account belongs to the host’s libvirt group. Enter a simple SSH username and a DNS name or IPv4 address in the same terminal, without URI punctuation. Do not expose an unauthenticated guest console directly to the network.

read -r -p 'SSH user on the KVM host: ' KVM_USER

Keep the same terminal open, then enter the host name or IPv4 address:

read -r -p 'KVM host DNS name or IPv4 address: ' KVM_HOST

Validate the two URI components before opening the guest console:

if [ -z "$KVM_USER" ] || [ -z "$KVM_HOST" ]; then
  printf 'Both the SSH user and KVM host are required.\n' >&2
elif printf '%s\n' "$KVM_USER" | grep -qvE '^[A-Za-z0-9_][A-Za-z0-9._-]*$'; then
  printf 'The SSH user contains unsupported URI characters.\n' >&2
elif printf '%s\n' "$KVM_HOST" | grep -qvE '^[A-Za-z0-9][A-Za-z0-9.-]*[A-Za-z0-9]$|^[A-Za-z0-9]$'; then
  printf 'Use a DNS name or IPv4 address without URI punctuation.\n' >&2
elif ! getent ahosts "$KVM_HOST" >/dev/null 2>&1; then
  printf 'The KVM host does not resolve through the current network configuration.\n' >&2
else
  virt-viewer --connect "qemu+ssh://${KVM_USER}@${KVM_HOST}/system" ubuntu-guest
fi

Manage KVM Guests with virsh

List the complete system guest inventory before running a lifecycle command:

virsh --connect qemu:///system list --all

Use the fixed guest name ubuntu-guest in these commands. Change the assignment once if the VM has a different name, then inspect its definition:

VM_NAME='ubuntu-guest'
virsh --connect qemu:///system dominfo "$VM_NAME"

Start a stopped guest:

VM_NAME='ubuntu-guest'
virsh --connect qemu:///system start "$VM_NAME"

Request a clean shutdown through the guest’s virtual power button:

VM_NAME='ubuntu-guest'
virsh --connect qemu:///system shutdown "$VM_NAME"

Enable automatic startup for a guest that should run when the host boots:

VM_NAME='ubuntu-guest'
virsh --connect qemu:///system autostart "$VM_NAME"

A graceful shutdown depends on the guest operating system responding to the ACPI request. Check the state with virsh --connect qemu:///system list --all before considering a forced power-off.

Update KVM and libvirt on Ubuntu

KVM, QEMU, libvirt, and the optional management tools remain owned by Ubuntu’s configured APT repositories. Refresh metadata and review the pending updates:

sudo apt update
apt list --upgradable

Apply reviewed updates through the normal Ubuntu upgrade transaction:

sudo apt upgrade

Already-running guests can continue using their pre-update QEMU processes until those guests restart. Kernel updates require a host reboot before the new KVM modules become active. Check whether Ubuntu recorded a reboot requirement:

if [ -f /var/run/reboot-required ]; then
  cat /var/run/reboot-required
else
  printf 'No reboot is currently recorded as required.\n'
fi

Do not restart the virtualisation stack or reboot a production host without checking running guests and arranging their shutdown or migration first.

Remove KVM from Ubuntu

Package removal does not delete virtual machine definitions, disk images, ISO files, or firmware state. List the guests and their storage before removing the management stack:

virsh --connect qemu:///system list --all
virsh --connect qemu:///system pool-list --all

Back up or deliberately remove every required guest before uninstalling libvirt. Files under /var/lib/libvirt/ can contain complete virtual disks, UEFI variable stores, snapshots, and other guest state. Do not delete that directory as a routine package-removal step.

After stopping or removing every guest that uses the default network, stop that network if it is active. This closes its managed dnsmasq processes and removes virbr0 before the libvirt packages disappear:

if virsh --connect qemu:///system net-info default 2>/dev/null |
  grep -Eq '^Active:[[:space:]]+yes$'; then
  virsh --connect qemu:///system net-destroy default
fi

Remove the optional graphical and command-line tools first when they are no longer needed:

sudo apt remove virt-manager virt-viewer virtinst qemu-system-gui swtpm swtpm-tools

Then remove the system libvirt deployment, command-line clients, and hardware checker:

sudo apt remove --purge libvirt-daemon-system libvirt-clients cpu-checker

The QEMU provider, disk utilities, UEFI firmware, and block backends can support workloads outside libvirt. Inspect each package before removing it:

for PACKAGE in ovmf qemu-system-x86 qemu-utils qemu-block-extra; do
  if ! dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package} ${Version}\n' \
    "$PACKAGE" 2>/dev/null; then
    printf '%s is not installed.\n' "$PACKAGE"
  fi
done

Remove these packages only when no other emulator, firmware, image-conversion, or virtualisation workflow depends on them. Keep qemu-block-extra in the same transaction as qemu-system-x86; on Ubuntu 22.04, removing the provider first can make APT select qemu-system-x86-xen to satisfy the remaining block package:

sudo apt remove ovmf qemu-system-x86 qemu-utils qemu-block-extra

Simulate the autoremove transaction and review every dependency that APT considers unused:

apt -s autoremove

If the simulation includes systemd-container and no other virtual-machine or container workflow needs it, stop its active systemd-machined process before the package disappears. This prevents the process from outliving its unit file on Ubuntu 24.04 or 22.04:

if systemctl is-active --quiet systemd-machined.service; then
  sudo systemctl stop systemd-machined.service
fi

Apply the reviewed transaction without auto-confirming it, then check for failed units:

sudo apt autoremove
systemctl --failed

If systemd-machined.service is the only failed entry, its load state is not-found, and the completed transaction confirms that systemd-container was removed, stop any retained process before reloading systemd and clear that exact stale record:

sudo systemctl stop systemd-machined.service 2>/dev/null || true
sudo systemctl daemon-reload
sudo systemctl reset-failed systemd-machined.service
systemctl --failed

Remove the current account from the libvirt group only when it no longer manages any system libvirt host:

if getent group libvirt >/dev/null && id -nG | tr ' ' '\n' | grep -qx libvirt; then
  sudo deluser "$USER" libvirt
fi

Troubleshoot KVM on Ubuntu

KVM Acceleration Cannot Be Used

Run the capability check and inspect recent kernel messages:

sudo kvm-ok
sudo dmesg | grep -iE 'kvm|vmx|svm' | tail -n 30

On bare metal, enable Intel VT-x or AMD-V in the system firmware. On an Ubuntu VM, enable nested virtualisation in the outer hypervisor and confirm that it exposes /dev/kvm. A CPU flag alone is not enough when the device is absent or firmware has disabled virtualisation.

Permission Denied for qemu:///system

Confirm the current login session has the required group and reconnect explicitly to the system scope:

id -nG
virsh --connect qemu:///system list --all

If libvirt is missing, add the user again, then end the complete desktop or SSH session and start a new one. Switching silently to qemu:///session can hide the permission problem while creating a separate set of guests and networks.

virsh Cannot Connect to libvirt

Confirm that the core packages remain installed, then retry the functional connection:

dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package} ${Version}\n' \
  libvirt-daemon-system libvirt-clients
virsh --connect qemu:///system list --all

Review recent libvirt and QEMU messages without assuming one daemon unit name across every supported Ubuntu release. For narrower unit, boot, priority, or time filters, use these journalctl command examples:

sudo journalctl --since '10 minutes ago' | grep -Ei 'libvirt|virtqemud|qemu'

Ubuntu’s libvirt service layout has changed over time, so a functional virsh connection is a stronger cross-release check than relying on one hard-coded systemctl status command.

The Default Network Is Missing or Inactive

Inspect the complete network inventory before changing its state:

virsh --connect qemu:///system net-list --all

If default is listed but inactive, enable it at boot and start it now:

virsh --connect qemu:///system net-autostart default
virsh --connect qemu:///system net-start default

If default is absent, reinstall the package that supplies its XML, define the packaged network, then change its startup state only when the reported value requires it:

sudo apt install --reinstall libvirt-daemon-config-network
virsh --connect qemu:///system net-define /usr/share/libvirt/networks/default.xml

if ! virsh --connect qemu:///system net-info default |
  grep -E '^Autostart:[[:space:]]+yes$' >/dev/null; then
  virsh --connect qemu:///system net-autostart default
fi

if ! virsh --connect qemu:///system net-info default |
  grep -E '^Active:[[:space:]]+yes$' >/dev/null; then
  virsh --connect qemu:///system net-start default
fi

Recheck the inventory after either fix. The default network should be active and configured to start automatically:

virsh --connect qemu:///system net-list --all

Do not replace the private NAT network with a host bridge merely to clear an installation error. Bridging changes the host’s physical network path and solves a different requirement.

AppArmor Logs a Fresh Denial for an ISO or Disk

Ubuntu launches libvirt-managed QEMU guests under AppArmor isolation. Keep normal installation media and disks under /var/lib/libvirt/images/, then inspect recent denials when a non-standard path fails:

sudo journalctl -k --since '10 minutes ago' | grep -i 'apparmor="DENIED"'

A fresh denial that names the failed guest and exact path is evidence to investigate AppArmor; an older or unrelated denial is not. First confirm the path, ownership, permissions, and intended access, move ordinary installation media into /var/lib/libvirt/images/, then retry the same guest action.

Do not edit the generated guest profiles under /etc/apparmor.d/libvirt/. If a non-standard path is genuinely required and a fresh denial confirms the block, use the package-provided local includes under /etc/apparmor.d/local/abstractions/libvirt-qemu or /etc/apparmor.d/local/usr.lib.libvirt.virt-aa-helper. The required rule is specific to the path and operation, so avoid generic broad allowances; parse and reload the affected profile, retest the guest, and keep a rollback that removes only the local rule.

virt-manager Does Not Open on Ubuntu Server

virt-manager requires a graphical session. Keep the KVM host headless and manage it with virsh, or run virt-manager or virt-viewer on a separate workstation through an SSH-backed libvirt connection. Installing an entire desktop on a production host only to open the manager increases the system’s package and attack surface.

Conclusion

KVM is installed with Ubuntu-managed QEMU and libvirt packages, hardware acceleration is checked through /dev/kvm, and the host can manage system guests through virsh, virt-manager, or virt-install. Keep the default NAT network for ordinary guests, add UEFI or TPM packages only when required, and consider VirtualBox on Ubuntu only when a desktop-focused cross-platform hypervisor better matches the workload.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show our tutorials more often in Top Stories and mark them as preferred in AI Mode and AI Overviews when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Add to the discussion

Questions, fixes, command output, and version notes help keep this guide current.

Verify before posting: