You need a VM up fast, and the GUI isn't an option
You are setting up a headless test server, or your desktop session crashed and you need to spin up a guest to debug a network issue. You open the terminal and type virsh, expecting a simple command to create a machine. Instead, you get a help menu listing domains, networks, and storage pools without explaining how to actually get a VM running. The command line is the right tool for reproducibility and automation, but the initial setup feels scattered across three different utilities. You need a clear path from an empty host to a booting guest without guessing at XML syntax.
What's actually happening
Fedora's virtualization stack separates concerns to keep the system modular and secure. libvirt is the management layer. It provides a stable API and a daemon (libvirtd) that talks to the hypervisor. qemu-kvm is the hypervisor itself, handling the actual emulation and hardware virtualization. virt-install is a helper script that generates the XML configuration and creates the disk image. virsh is the command-line interface to manage the lifecycle of domains once they exist.
Think of libvirt as the foreman on a construction site. The foreman doesn't lay bricks. The foreman tells the workers what to do. qemu is the worker doing the heavy lifting. virt-install is the architect who draws the blueprints and orders the materials. virsh is the radio you use to talk to the foreman. You cannot ask the foreman to build a house from scratch without blueprints. You need the architect first, or you need to write the blueprints yourself.
virt-install bridges the gap between a command line and the XML definition that libvirt requires. It handles disk creation, network attachment, and console configuration, then registers the domain with libvirt. Once the domain is defined, virsh takes over for starting, stopping, and inspecting the VM.
Check journalctl -xeu libvirtd if the daemon misbehaves. The x flag adds explanatory context to error lines, and e jumps to the end of the log. Most sysadmins type this muscle-memory style when troubleshooting service failures.
Prerequisites and service setup
Install the hypervisor, the management library, and the installation helper. Enable the libvirtd service to start immediately and persist across reboots.
sudo dnf install qemu-kvm libvirt virt-install -y
# Install the KVM hypervisor, the libvirt management stack, and the installation helper
sudo systemctl enable --now libvirtd
# Start the daemon immediately and ensure it starts on boot
Verify the service is active. If the service fails to start, check the journal for dependency errors or port conflicts.
systemctl status libvirtd
# Show the current state, recent log lines, and dependency tree for the daemon
Reboot the host if libvirtd refuses to start after a crash. The daemon sometimes holds stale locks in /var/run/libvirt that prevent a clean restart.
Creating a VM with virt-install
Use virt-install to create a new domain. This command generates the XML definition, creates the disk image, and registers the VM with libvirt. The example below creates a guest named fedora-guest with 2GB of RAM, 2 virtual CPUs, and a 20GB disk, booting from a local ISO.
sudo virt-install \
--name fedora-guest \
--memory 2048 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/fedora-guest.qcow2,size=20 \
--cdrom /path/to/Fedora-Workstation-Live-x86_64.iso \
--network network=default \
--graphics vnc,listen=0.0.0.0 \
--boot cdrom
# --graphics vnc exposes the console on port 5900 by default for remote access
# --network network=default attaches the VM to the libvirt NAT network
# --boot cdrom ensures the guest boots from the installation media first
The --disk flag creates a qcow2 image by default. This format supports snapshots, compression, and thin provisioning. The file starts small and grows as data is written. If you need a raw disk image for specific performance requirements, add format=raw to the disk option. virt-install will refuse to overwrite an existing disk file unless you pass --force.
Check the disk size before you boot. A full disk image causes silent hangs during guest installation, and the error messages inside the VM are often unhelpful.
Managing the domain with virsh
Once virt-install completes, the domain is defined but not running. Use virsh to manage the lifecycle. List all domains to see the state, start the guest, and attach to the console.
virsh list --all
# Show all domains, including those that are shut off or paused
virsh start fedora-guest
# Boot the domain without destroying existing state
virsh console fedora-guest
# Attach to the serial console if configured, otherwise use VNC for graphical access
The virsh console command connects to the serial port. For graphical installations, you need a VNC client pointing at the host IP on port 5900. The --graphics vnc flag in virt-install opens this port. If you are on a headless server, add --graphics none to virt-install and use --console pty,target_type=serial to enable text-based installation via the serial console.
Run virsh net-autostart default after creating your first VM. The NAT network does not survive a host reboot by default, and guests will lose connectivity until you manually start the network.
Defining a VM manually from XML
You can define a domain from a raw XML file if you need precise control over hardware topology or are migrating a definition from another host. Create the XML file, define the domain, and start it.
sudo virsh define /path/to/custom-guest.xml
# Load the XML definition into libvirt without starting the domain
sudo virsh start custom-guest
# Boot the newly defined domain
Config files in /etc/libvirt are user-modified. Files in /usr/share/libvirt ship with the package. Edit /etc. Never edit /usr/share. Manual edits in the package directory get overwritten on the next dnf upgrade.
Verify the configuration
Confirm the domain is running and inspect the resource allocation. Check the network interfaces to ensure the guest is attached to the correct virtual network.
virsh dominfo fedora-guest
# Display detailed state, memory, and CPU allocation for the domain
virsh domiflist fedora-guest
# List network interfaces and their connection to virtual networks
If the domain shows as running but you cannot ping it, verify the virtual network is active. Run virsh net-list --all to check the state of all networks. Start the default network with virsh net-start default if it is inactive.
Common pitfalls and error recovery
SELinux denials are the most common blocker for custom disk paths. If the VM fails to start with a generic error, check the audit log for access vector cache denials. Fedora handles /var/lib/libvirt contexts automatically, but custom paths require manual labeling.
sudo ausearch -m avc -ts recent
# Search the audit log for recent SELinux access vector cache denials
sudo semanage fcontext -a -t virt_image_t "/custom/path(/.*)?"
# Add a file context rule so restorecon knows how to label the path
sudo restorecon -Rv /custom/path
# Apply the new context recursively to existing files
The Error: internal error: process exited while connecting to monitor is the classic symptom of a missing kernel module or a permissions error. Run dmesg | tail to check for kvm errors. If the kernel module is not loaded, run sudo modprobe kvm and verify with lsmod | grep kvm.
Firewall rules block VNC access by default. Open the port in firewalld and reload the configuration.
sudo firewall-cmd --permanent --add-port=5900/tcp
# Allow VNC traffic on port 5900 in the persistent configuration
sudo firewall-cmd --reload
# Apply the persistent rules to the runtime configuration immediately
firewall-cmd --reload is mandatory after every rule change. The runtime config and the persistent config diverge otherwise, and rules disappear on reboot.
Decision matrix
Use virt-install when you are creating a new VM from an ISO or cloud image and want the tool to handle disk creation and XML generation. Use virsh define when you have a hand-crafted XML configuration or are importing a domain definition from a backup. Use virt-clone when you need to duplicate an existing VM with a new MAC address and disk image. Use virt-manager when you prefer a graphical interface for initial setup but still want the underlying libvirt backend. Use qemu-kvm directly only when you are debugging low-level emulation issues or bypassing libvirt for a specific test.
Trust the package manager. Manual file edits drift, snapshots stay.