โ† Back to Tutorials
5 May 2026ยท4 min read

libvirt & virsh Basics: Manage KVM VMs by CLI

Control KVM virtual machines from the command line with virsh: list, start, stop, snapshot, edit domain XML, and manage libvirt networks and storage pools.

Once KVM is running, virsh becomes the command-line cockpit for everything libvirt manages: virtual machines (called domains), virtual networks, and storage pools. It is scriptable, fast, and works perfectly over SSH on a headless server โ€” which is exactly why orchestration layers like OpenStack drive libvirt under the hood rather than clicking through a GUI.

This tutorial covers the practical virsh commands you will use every day: listing and controlling domains, inspecting and editing domain XML, taking snapshots, and managing networks and storage pools. It assumes you already have a working KVM/QEMU host โ€” if not, start with our guide on installing KVM/QEMU on Ubuntu 24.04.

Prerequisites

You need Ubuntu 24.04 with qemu-kvm, libvirt-daemon-system, and libvirt-clients installed, your user in the libvirt group, and at least one VM defined (the examples use a domain named ubuntu-test). Confirm the daemon is up with systemctl status libvirtd.

Understanding the connection URI

virsh talks to a libvirt daemon over a connection URI. For local system VMs, that is qemu:///system. Set it once per shell so you can omit --connect on every command:

export LIBVIRT_DEFAULT_URI=qemu:///system
virsh uri

Listing and inspecting domains

List running domains, or all defined domains including stopped ones:

virsh list
virsh list --all

Get detailed information about a single domain โ€” state, vCPUs, memory, and the autostart flag:

virsh dominfo ubuntu-test

Inspect live resource usage and the virtual hardware attached to a domain:

virsh domstats ubuntu-test
virsh domblklist ubuntu-test
virsh domiflist ubuntu-test

Controlling VM lifecycle

The core lifecycle verbs are intuitive. shutdown asks the guest OS to power off gracefully via ACPI, while destroy is the equivalent of pulling the power cord โ€” use it only when a guest is hung:

virsh start ubuntu-test
virsh shutdown ubuntu-test
virsh reboot ubuntu-test
virsh destroy ubuntu-test

To suspend a VM to RAM and resume it later, or to make a domain start automatically with the host:

virsh suspend ubuntu-test
virsh resume ubuntu-test
virsh autostart ubuntu-test
virsh autostart --disable ubuntu-test

Editing domain XML

Every domain is defined by an XML document describing its CPU, memory, disks, and NICs. View it, or open it in your editor with on-save validation:

virsh dumpxml ubuntu-test
virsh edit ubuntu-test

Changes to a running domain take effect on the next boot. For a quick live adjustment, set the active memory allocation and vCPU count without editing XML:

virsh setmem ubuntu-test 4194304 --live
virsh setvcpus ubuntu-test 4 --live

To define a brand-new domain from an XML file you have prepared, or to remove a domain definition (its disk is untouched):

virsh define mydomain.xml
virsh undefine ubuntu-test

Working with snapshots

Snapshots capture a point-in-time state you can roll back to โ€” invaluable before risky upgrades. Create one, list them, and revert:

virsh snapshot-create-as ubuntu-test snap1 'Before kernel upgrade'
virsh snapshot-list ubuntu-test
virsh snapshot-revert ubuntu-test snap1
virsh snapshot-delete ubuntu-test snap1

Note that internal snapshots work with qcow2 disks. For raw or RBD-backed disks the workflow differs, which is one reason production clouds favour storage-layer snapshots.

Managing virtual networks

libvirt ships a default NAT network. List networks, inspect one, and control its lifecycle:

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

To see which IP addresses libvirt's DHCP has handed out on a network:

virsh net-dhcp-leases default

Managing storage pools and volumes

Storage pools are directories or devices libvirt allocates disk images from. List pools, inspect the default, and create a volume:

virsh pool-list --all
virsh pool-info default
virsh vol-create-as default data.qcow2 10G --format qcow2
virsh vol-list default

Attach that volume to a running VM as a new virtual disk, persisting the change across reboots:

virsh attach-disk ubuntu-test \
  /var/lib/libvirt/images/data.qcow2 vdb \
  --persistent --subdriver qcow2

Connecting to the console

To reach a VM's serial console directly from the terminal (handy over SSH when there is no network access to the guest yet):

virsh console ubuntu-test

Press Ctrl+] to exit the console. If nothing appears, the guest may need a serial console enabled on its kernel command line (console=ttyS0).

Troubleshooting and pitfalls

  • โ€˜failed to connect to the hypervisorโ€™ โ€” you are targeting the wrong URI; export LIBVIRT_DEFAULT_URI=qemu:///system or your VMs will appear under your unprivileged user session.
  • Empty domain list โ€” almost always the session-vs-system URI confusion above; the VMs exist, you are just looking at the wrong scope.
  • XML edits silently reverted โ€” you edited a running domain; changes apply on next full shutdown and start, not a reboot from inside the guest.
  • Snapshot errors on raw disks โ€” internal snapshots require qcow2; convert the disk or use external snapshots.

Where to go next

You now control KVM entirely from the CLI โ€” the exact layer OpenStack automates at scale. To give your domains production-grade, replicated storage, continue with using Ceph RBD as KVM/libvirt storage.

Conclusion

virsh turns a KVM host into a fully manageable, scriptable virtualization platform without ever opening a GUI. Mastering domain lifecycle, XML editing, snapshots, networks, and pools is the same mental model clouditiv operators use โ€” only we drive thousands of these domains through OpenStack so you do not have to. If hand-managing libvirt across a fleet sounds like a lot, clouditiv runs a sovereign, ISO 27001 / BSI C5 private cloud on Ubuntu 24.04 + OpenStack 2025.2 with data in Germany. See our on-premise cloud solution for the managed path.