If you manage KVM virtual machines in production, virsh is the tool you will reach for more than anything else. It is the primary command-line interface for libvirt and gives you full control over every aspect of a guest VM, from creation to migration to teardown. This reference covers every virsh operation I use regularly after 10+ years of running KVM hosts in production environments, organized by task category with real command examples and expected output.

All commands below assume you are running as root or using a user with proper libvirt group membership. The default connection URI is qemu:///system. If you need to connect to a remote host, append -c qemu+ssh://root@remotehost/system to any virsh command.

Prerequisites

You need a working KVM hypervisor with libvirt installed and the libvirtd service running. If you have not set this up yet, follow our guide on how to install KVM virtualization on Linux. Verify your environment is ready:

$ virsh version
Compiled against library: libvirt 9.0.0
Using library: libvirt 9.0.0
Using API: QEMU 9.0.0
Running hypervisor: QEMU 7.2.0

1. VM Lifecycle Management

These are the bread and butter commands for controlling the state of your virtual machines. You will use these dozens of times a day on a busy hypervisor.

Define a VM from XML

Defining a VM registers it with libvirt without starting it. This is how you import a VM configuration or create one from a prepared XML file.

$ virsh define /etc/libvirt/qemu/webserver01.xml
Domain 'webserver01' defined from /etc/libvirt/qemu/webserver01.xml

Start, Shutdown, Reboot, and Force Stop

Starting a VM boots it from a powered-off state. Shutdown sends an ACPI signal to the guest OS for a clean poweroff. Reboot does the same but brings the guest back up. If a guest is unresponsive and will not shut down cleanly, destroy pulls the plug immediately, similar to yanking the power cord on a physical server.

$ virsh start webserver01
Domain 'webserver01' started

$ virsh shutdown webserver01
Domain 'webserver01' is being shut down

$ virsh reboot webserver01
Domain 'webserver01' is being rebooted

$ virsh destroy webserver01
Domain 'webserver01' destroyed

A common mistake is using destroy when you mean shutdown. The name is misleading. Destroy does not delete the VM or its disks. It is the equivalent of a hard power-off. The VM definition and storage remain intact.

Suspend and Resume

Suspend pauses the VM and frees its CPU cycles while keeping it in memory. Resume brings it back instantly. This is useful during host maintenance windows when you need to briefly pause workloads without a full reboot cycle.

$ virsh suspend webserver01
Domain 'webserver01' suspended

$ virsh resume webserver01
Domain 'webserver01' resumed

Undefine (Remove) a VM

Undefine removes the VM definition from libvirt. The VM must be shut down first. Add --remove-all-storage to also delete the associated disk images. Without that flag, the disk files remain on the filesystem.

$ virsh undefine webserver01
Domain 'webserver01' has been undefined

$ virsh undefine webserver01 --remove-all-storage --nvram
Domain 'webserver01' has been undefined
Volumes removed: /var/lib/libvirt/images/webserver01.qcow2

2. VM Information and Status

Before making changes to any VM, always check its current state. These commands give you the full picture of what is running and how it is configured.

List VMs

The most frequently used virsh command. Without flags, it only shows running VMs. Always use --all to include stopped guests.

$ virsh list --all
 Id   Name            State
---------------------------------
 1    webserver01     running
 3    dbserver01      running
 -    testvm02        shut off
 -    staging-app     shut off

Detailed VM Information

Use dominfo to get a quick summary of a VM’s resource allocation and state. For block devices and network interfaces, use domblklist and domiflist.

$ virsh dominfo webserver01
Id:             1
Name:           webserver01
UUID:           a3f2b8c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c
OS Type:        hvm
State:          running
CPU(s):         4
CPU time:       1245.3s
Max memory:     8388608 KiB
Used memory:    8388608 KiB
Persistent:     yes
Autostart:      enable
Managed save:   no
Security model: apparmor
Security DOI:   0
$ virsh domblklist webserver01
 Target   Source
---------------------------------------------------
 vda      /var/lib/libvirt/images/webserver01.qcow2
 sda      /var/lib/libvirt/images/data01.qcow2

To see which network interfaces are attached:

$ virsh domiflist webserver01
 Interface   Type      Source    Model    MAC
--------------------------------------------------------------
 vnet0       bridge    br0      virtio   52:54:00:a1:b2:c3
 vnet1       network   default  virtio   52:54:00:d4:e5:f6

Performance Stats and Full XML Dump

For performance troubleshooting, domstats provides CPU, memory, block I/O, and network counters in one shot. To export the complete VM configuration, use dumpxml.

$ virsh domstats webserver01 --cpu-total --block --interface
Domain: 'webserver01'
  cpu.time=124530000000
  cpu.user=98200000000
  cpu.system=26330000000
  block.count=1
  block.0.name=vda
  block.0.rd.reqs=45230
  block.0.rd.bytes=1845006336
  block.0.wr.reqs=12840
  block.0.wr.bytes=524288000
  net.count=1
  net.0.name=vnet0
  net.0.rx.bytes=2048576000
  net.0.tx.bytes=1024288000
$ virsh dumpxml webserver01 > /tmp/webserver01-backup.xml

I always dump the XML before making any changes. It takes two seconds and has saved me from bad edits more times than I can count.

3. Storage Management

Virsh handles storage through pools and volumes. A pool is a directory, LVM volume group, NFS mount, or other storage backend. Volumes are the individual disk images within a pool.

Storage Pools

$ virsh pool-list --all
 Name       State    Autostart
---------------------------------
 default    active   yes
 images     active   yes
 backups    active   no

$ virsh pool-info default
Name:           default
UUID:           b1c2d3e4-f5a6-7b8c-9d0e-1f2a3b4c5d6e
State:          running
Persistent:     yes
Autostart:      yes
Capacity:       500.00 GiB
Allocation:     312.45 GiB
Available:      187.55 GiB

Volume Operations

List existing volumes in a pool, create new ones, or remove volumes you no longer need.

$ virsh vol-list default
 Name                    Path
---------------------------------------------------------------
 webserver01.qcow2       /var/lib/libvirt/images/webserver01.qcow2
 dbserver01.qcow2        /var/lib/libvirt/images/dbserver01.qcow2

$ virsh vol-create-as default newdisk01.qcow2 50G --format qcow2
Vol newdisk01.qcow2 created

$ virsh vol-delete newdisk01.qcow2 --pool default
Vol newdisk01.qcow2 deleted

Hot-Attach a Disk to a Running VM

You can attach additional disks without downtime. The --persistent flag ensures the disk survives a reboot. Without it, the disk attachment is lost when the VM shuts down.

$ virsh attach-disk webserver01 /var/lib/libvirt/images/data02.qcow2 vdb \
  --driver qemu --subdriver qcow2 --persistent
Disk attached successfully

$ virsh detach-disk webserver01 vdb --persistent
Disk detached successfully

4. Networking

KVM networking through libvirt supports NAT, bridged, macvtap, and isolated networks. For production servers, bridged networking is standard, but the default NAT network works fine for lab and development environments.

Network Listing and Details

$ virsh net-list --all
 Name       State    Autostart   Persistent
----------------------------------------------
 default    active   yes         yes
 isolated   active   no          yes
 br-prod    active   yes         yes

$ virsh net-info default
Name:           default
UUID:           c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f
Active:         yes
Persistent:     yes
Autostart:      yes
Bridge:         virbr0

Get Guest IP Addresses

This is one of the most useful commands when you spin up a new VM and need to SSH into it. It queries the DHCP lease database or the guest agent for IP information.

$ virsh domifaddr webserver01
 Name       MAC address          Protocol   Address
--------------------------------------------------------------
 vnet0      52:54:00:a1:b2:c3    ipv4       192.168.122.45/24

$ virsh domifaddr webserver01 --source agent
 Name       MAC address          Protocol   Address
--------------------------------------------------------------
 lo         00:00:00:00:00:00    ipv4       127.0.0.1/8
 eth0       52:54:00:a1:b2:c3    ipv4       192.168.122.45/24
 eth0       52:54:00:a1:b2:c3    ipv6       fe80::5054:ff:fea1:b2c3/64

Attach and Detach Network Interfaces

Add or remove network interfaces on the fly. This is particularly useful when migrating a VM to a different network segment.

$ virsh attach-interface webserver01 bridge br1 --model virtio --persistent
Interface attached successfully

$ virsh detach-interface webserver01 bridge --mac 52:54:00:d4:e5:f6 --persistent
Interface detached successfully

5. Snapshots

Snapshots capture the state of a VM at a point in time. They are invaluable before applying patches, running upgrades, or testing configuration changes. For detailed coverage of snapshot strategies, see our guide on managing KVM virtual machines with virsh.

Create and List Snapshots

$ virsh snapshot-create-as webserver01 --name "pre-upgrade-2025" \
  --description "Before kernel upgrade"
Domain snapshot pre-upgrade-2025 created

$ virsh snapshot-list webserver01
 Name                 Creation Time               State
-------------------------------------------------------------
 pre-upgrade-2025     2025-09-15 14:30:22 +0000   running
 baseline-config      2025-08-01 09:00:00 +0000   shutoff

Revert and Delete Snapshots

Reverting rolls the VM back to the exact state captured in the snapshot. The current state is lost unless you snapshot it first. Always verify which snapshot you are reverting to before running the command.

$ virsh snapshot-revert webserver01 pre-upgrade-2025
Domain snapshot pre-upgrade-2025 reverted

$ virsh snapshot-delete webserver01 pre-upgrade-2025
Domain snapshot pre-upgrade-2025 deleted

Keep in mind that internal snapshots on qcow2 images grow the file size over time and can hurt I/O performance. Clean up old snapshots regularly.

6. Console Access

When networking is down or not yet configured on a guest, you need console access to troubleshoot. Virsh provides multiple options.

Serial Console

This connects to the serial console of the guest. The guest OS must have a serial console configured (for example, adding console=ttyS0,115200 to the kernel boot parameters). Press Ctrl+] to disconnect.

$ virsh console webserver01
Connected to domain 'webserver01'
Escape character is ^]

Ubuntu 22.04.3 LTS webserver01 ttyS0
webserver01 login:

VNC Display and Screenshots

If the guest uses a graphical console, find the VNC display port and connect with any VNC client. You can also capture a screenshot of the current display directly from the command line.

$ virsh vncdisplay webserver01
127.0.0.1:0

$ virsh screenshot webserver01 /tmp/webserver01-screen.ppm
Screenshot saved to /tmp/webserver01-screen.ppm

7. Live Migration

Live migration moves a running VM from one KVM host to another with minimal downtime. Both hosts must have shared storage (or you use --copy-storage-all), compatible CPU models, and SSH key-based authentication configured between them.

$ virsh migrate --live --persistent --tunnelled webserver01 \
  qemu+ssh://kvmhost02.example.com/system
Migration completed successfully

$ virsh migrate --live --persistent --copy-storage-all webserver01 \
  qemu+ssh://kvmhost02.example.com/system --verbose
Migration: [100 %]

The --tunnelled flag encrypts the migration traffic through the libvirt connection. Use it when migrating across untrusted networks. The --persistent flag ensures the VM definition is created on the destination host. Without it, the VM becomes transient on the target and disappears on shutdown.

For large memory VMs, you may need to increase the migration bandwidth and set a downtime tolerance:

$ virsh migrate-setmaxdowntime webserver01 500
$ virsh migrate-setspeed webserver01 1000

8. Resource Management

One of the key advantages of virtualization is the ability to adjust resources without rebuilding a machine. Virsh lets you tune CPU, memory, and other resources on the fly or in the persistent configuration.

CPU Management

You can hot-add vCPUs to a running VM (if the guest OS supports it) or change the persistent configuration for next boot. The maximum must be set in the VM definition first.

$ virsh setvcpus webserver01 6 --live
$ virsh setvcpus webserver01 6 --config

$ virsh vcpucount webserver01
maximum      config         8
maximum      live           8
current      config         6
current      live           6

$ virsh cpu-stats webserver01
CPU0:
  cpu_time:    45230000000 ns
  user_time:   32100000000 ns
  system_time: 13130000000 ns
CPU1:
  cpu_time:    38900000000 ns
  user_time:   27800000000 ns
  system_time: 11100000000 ns

Memory Management

Memory ballooning allows you to adjust guest memory without a reboot, provided the balloon driver is loaded in the guest. Set the maximum first in the config, then adjust the current allocation.

$ virsh setmaxmem webserver01 16G --config
$ virsh setmem webserver01 12G --live

$ virsh memtune webserver01
hard_limit     : unlimited
soft_limit     : unlimited
swap_hard_limit: unlimited
min_guarantee  : 0

$ virsh memtune webserver01 --hard-limit 16777216 --soft-limit 8388608

The memtune values are in KiB. Setting a hard_limit prevents the VM’s qemu process from consuming more memory than specified, which is useful for preventing OOM situations on the host.

9. Cloning Virtual Machines

Cloning creates an identical copy of an existing VM with a new name, new UUID, and new MAC addresses. The source VM must be shut down before cloning.

$ virt-clone --original webserver01 --name webserver02 \
  --file /var/lib/libvirt/images/webserver02.qcow2
Allocating 'webserver02.qcow2'                   | 50 GB  00:02:15
Clone 'webserver02' created successfully.

$ virsh list --all | grep webserver
 1    webserver01     running
 -    webserver02     shut off

After cloning, always boot the new VM and update the hostname, static IP addresses, and any host-specific identifiers like machine-id. On systemd-based systems, regenerate the machine ID:

$ rm /etc/machine-id && systemd-machine-id-setup

For creating VM templates and automated provisioning, check our guide on creating KVM VMs with virt-install.

10. Autostart and Boot Order

Production VMs should be set to autostart so they come back up automatically after a host reboot. This is easy to forget and causes unnecessary downtime during planned maintenance.

$ virsh autostart webserver01
Domain 'webserver01' marked as autostarted

$ virsh autostart --disable testvm02
Domain 'testvm02' unmarked as autostarted

$ virsh list --all --autostart
 Id   Name            State
---------------------------------
 1    webserver01     running
 3    dbserver01      running

To change the boot order (for example, to boot from a CD-ROM for OS installation), edit the VM’s XML and adjust the <boot> elements under <os>:

$ virsh edit webserver01
# In the editor, modify the <os> section:
# <os>
#   <boot dev='cdrom'/>
#   <boot dev='hd'/>
# </os>
Domain 'webserver01' XML configuration edited.

11. XML Editing and Configuration

Every VM in libvirt is defined by an XML document. Understanding how to work with this XML is essential for anything beyond basic operations. The virsh edit command opens the VM’s XML in your default editor with validation on save.

$ EDITOR=vim virsh edit webserver01

When you save and exit, libvirt validates the XML and applies the changes. If the XML is malformed, it will reject the changes and let you re-edit. This is safer than manually editing files in /etc/libvirt/qemu/.

To create a VM from a standalone XML file or re-import a backed-up configuration:

$ virsh define /tmp/webserver01-backup.xml
Domain 'webserver01' defined from /tmp/webserver01-backup.xml

$ virsh create /tmp/webserver01-backup.xml
Domain 'webserver01' created from /tmp/webserver01-backup.xml

The difference between define and create is important. Define registers the VM persistently without starting it. Create starts the VM immediately but makes it transient, meaning the definition is lost when the VM shuts down. In production, always use define followed by start.

12. Useful One-Liners for Daily Admin

These are the shell one-liners I keep in my notes and use constantly. They combine virsh with standard Unix tools to handle bulk operations and quick reporting.

Start all VMs that are currently shut off:

$ for vm in $(virsh list --all --state-shutoff --name); do virsh start "$vm"; done

Gracefully shut down all running VMs:

$ for vm in $(virsh list --state-running --name); do virsh shutdown "$vm"; done

List all VMs with their disk usage:

$ for vm in $(virsh list --all --name); do
    echo "=== $vm ==="
    virsh domblkinfo "$vm" vda 2>/dev/null
  done

Get IP addresses for all running VMs:

$ for vm in $(virsh list --state-running --name); do
    echo "$vm: $(virsh domifaddr "$vm" | awk 'NR>2 && NF{print $4}')"
  done

Export XML backups for all VMs:

$ mkdir -p /root/vm-xml-backups
$ for vm in $(virsh list --all --name); do
    virsh dumpxml "$vm" > "/root/vm-xml-backups/${vm}.xml"
  done

Find VMs consuming the most CPU time:

$ virsh list --state-running --name | while read vm; do
    cpu=$(virsh dominfo "$vm" | awk '/CPU time/{print $3}')
    echo "$cpu $vm"
  done | sort -rn | head -10

Check which VMs do NOT have autostart enabled:

$ virsh list --all --name --no-autostart

Quick Reference Table

For quick lookups, here are the most common virsh operations in one place:

TaskCommand
List all VMsvirsh list --all
Start a VMvirsh start <name>
Graceful shutdownvirsh shutdown <name>
Force power offvirsh destroy <name>
Get VM detailsvirsh dominfo <name>
Get guest IPvirsh domifaddr <name>
Create snapshotvirsh snapshot-create-as <name> --name <snap>
Revert snapshotvirsh snapshot-revert <name> <snap>
Live migratevirsh migrate --live <name> <dest-uri>
Set vCPUsvirsh setvcpus <name> <count> --live
Set memoryvirsh setmem <name> <size> --live
Enable autostartvirsh autostart <name>
Dump XML configvirsh dumpxml <name>
Edit VM configvirsh edit <name>
Clone a VMvirt-clone --original <name> --name <new> --auto-clone

Final Notes

Virsh is the single most important tool in a KVM administrator’s workflow. Every operation you can perform through virt-manager or a web UI ultimately translates to a virsh or libvirt API call underneath. Learning these commands thoroughly will make you faster, give you better control over your hypervisors, and let you automate anything through simple shell scripts.

A few habits that have served me well over the years: always dump the XML before editing a VM, always use --persistent when making live changes you want to keep, and always set autostart on production VMs. These three practices alone will prevent the majority of “it was working until we rebooted the host” incidents.

For the full virsh command reference, run virsh help or virsh help <command> for detailed usage on any specific subcommand.

6 COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here