Day: November 18, 2022
How to Deploy KVM and Use Libvirt to Create VMs from the CLI
KVM (Kernel-based Virtual Machine) with libvirt is my go-to stack when I want proper virtualization on Linux without the overhead of full GUI tools. If you’re running servers, building a lab, or automating VM provisioning, doing it all through the command line is cleaner, faster, and easier to script.
This guide covers everything from installation to deploying and managing virtual machines entirely from the terminal.
- Installing KVM and libvirt
- Verifying hardware virtualization
- Setting up networking and storage pools
- Deploying VMs with
virt-install - Managing VMs via
virsh - Cloning and templating VMs
- Automating deployments with scripts
1. Install KVM and Libvirt
1.1 Check Virtualization Support
egrep -c '(vmx|svm)' /proc/cpuinfo
If the output is 1 or higher, your CPU supports hardware virtualization. Then confirm the kernel modules are loaded:
lsmod | grep kvm
1.2 Install on RHEL / Rocky / Alma
sudo dnf install -y \
qemu-kvm \
libvirt \
libvirt-daemon \
libvirt-daemon-driver-qemu \
virt-install \
virt-manager \
bridge-utils
1.3 Install on Ubuntu / Debian
sudo apt update
sudo apt install -y \
qemu-kvm \
libvirt-daemon-system \
libvirt-clients \
virtinst \
bridge-utils
1.4 Enable and Start Libvirt
sudo systemctl enable --now libvirtd
sudo systemctl status libvirtd
If the service is active, KVM and libvirt are ready to go.
2. Verify Setup
2.1 Check Capabilities
virsh capabilities
2.2 List VMs
virsh list --all
2.3 Check Default Network
virsh net-list --all
If the default network exists but isn’t active:
virsh net-start default
virsh net-autostart default
3. Networking Options
There are two main ways to handle networking with libvirt:
- Use the default NAT network (simpler)
- Create a Linux bridge for LAN access
3.1 Option A: Default NAT
Libvirt’s built-in NAT network works fine for most setups. VMs will have private IPs and access the internet via NAT through the host. Nothing to configure here.
3.2 Option B: Create a Linux Bridge
For VMs that need to sit directly on your LAN, create a bridge and attach the NIC to it.
Example (RHEL/Rocky/Alma):
# /etc/sysconfig/network-scripts/ifcfg-br0
DEVICE=br0
TYPE=Bridge
BOOTPROTO=dhcp
ONBOOT=yes
# /etc/sysconfig/network-scripts/ifcfg-eno1
DEVICE=eno1
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
BRIDGE=br0
sudo systemctl restart NetworkManager
You can define it in libvirt as well if you want it persistent:
virsh net-define br0.xml
virsh net-start br0
virsh net-autostart br0
4. Storage Pools and Volumes
4.1 Check Default Pool
virsh pool-list --all
virsh pool-start default
virsh pool-autostart default
4.2 Create a Custom Pool
sudo mkdir -p /vm_storage/images
virsh pool-define-as \
vm_pool dir - - - - "/vm_storage/images"
virsh pool-start vm_pool
virsh pool-autostart vm_pool
4.3 Create a Disk Image
virsh vol-create-as vm_pool rocky8.qcow2 40G --format qcow2
5. Deploy VMs with virt-install
Example 1: Network Install (Rocky Linux)
virt-install \
--name rocky8 \
--ram 4096 \
--vcpus 2 \
--disk path=/vm_storage/images/rocky8.qcow2,size=40 \
--os-variant=rocky8 \
--network network=default \
--graphics none \
--location "https://dl.rockylinux.org/pub/rocky/8/BaseOS/x86_64/os/" \
--extra-args="console=ttyS0,115200n8 serial"
Example 2: Install from ISO
virt-install \
--name ubuntu-test \
--ram 4096 \
--vcpus 2 \
--disk path=/vm_storage/images/ubuntu-test.qcow2,size=40 \
--cdrom /isos/ubuntu-22.04.iso \
--network network=default \
--graphics vnc \
--os-variant ubuntu22.04
Example 3: Cloud-Init Image
virt-install \
--name cloud-ubuntu \
--ram 2048 \
--vcpus 2 \
--disk /vm_storage/images/ubuntu-cloud.qcow2 \
--cloud-init user-data=cloud-init.yaml \
--network network=default \
--os-variant ubuntu22.04 \
--graphics none
6. Managing VMs with virsh
virsh start rocky8
virsh shutdown rocky8
virsh destroy rocky8 # Force stop
virsh autostart rocky8
virsh console rocky8
virsh list --all
To remove a VM completely:
virsh destroy rocky8
virsh undefine rocky8 --remove-all-storage
7. Managing Networks
Create a new NAT network manually:
<network>
<name>mynet</name>
<bridge name='virbr20'/>
<forward mode='nat'/>
<ip address='192.168.50.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.50.10' end='192.168.50.100'/>
</dhcp>
</ip>
</network>
virsh net-define mynet.xml
virsh net-start mynet
virsh net-autostart mynet
8. Storage Volume Management
virsh vol-list vm_pool
virsh vol-resize /vm_storage/images/rocky8.qcow2 80G
9. Cloning and Templates
virt-sysprep -d rocky8
virt-clone --original rocky8 --name rocky8-clone --auto-clone
10. Automating Deployments
#!/bin/bash
VM=$1
DISK="/vm_storage/images/${VM}.qcow2"
ISO="/isos/rocky.iso"
virt-install \
--name "$VM" \
--ram 2048 \
--vcpus 2 \
--disk "$DISK",size=20 \
--cdrom "$ISO" \
--network network=default \
--os-variant rocky8 \
--graphics none \
--extra-args="console=ttyS0"
Conclusion
KVM with libvirt gives you a complete virtualization stack that’s fast, stable, and fully automatable. Everything can be controlled from the command line ideal for headless servers, automation pipelines, and anyone who prefers to keep infrastructure clean and scriptable. Once you’re comfortable with virsh and virt-install, managing dozens of VMs becomes trivial; great open source solution.
