Title
“Creating and Customizing Virtual Machines with NUMA Awareness on Modern Linux (Fedora/Ubuntu 2025)”
Introduction
Virtualization has evolved significantly, and tools like libvirt, virt-manager, and QEMU/KVM now allow fine-grained control over CPU topology, NUMA nodes, and memory allocation. In this guide, we’ll create a fully functional VM using both GUI and CLI methods, compatible with Fedora 42 and Ubuntu 24.04 LTS. We’ll also explore how to assign NUMA nodes, distribute vCPUs and memory, and customize devices and networks.
1. Install Dependencies
Fedora
sudo dnf install -y @virtualization libvirt virt-install virt-manager qemu-kvm
sudo systemctl enable --now libvirtd
Ubuntu
sudo apt update
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients virt-manager bridge-utils
sudo systemctl enable --now libvirtd
Check KVM support:
egrep -c '(vmx|svm)' /proc/cpuinfo
2. Create a Storage Pool and Network
Create a storage pool
sudo mkdir -p /var/lib/libvirt/images
sudo virsh pool-define-as default dir - - - - "/var/lib/libvirt/images"
sudo virsh pool-autostart default
sudo virsh pool-start default
Verify default NAT network
sudo virsh net-list --all
If missing:
sudo virsh net-define /usr/share/libvirt/networks/default.xml
sudo virsh net-autostart default
sudo virsh net-start default
3. Create a Virtual Machine (Basic)
Example for Fedora 42:
virt-install \
--name testvm \
--vcpus 8 \
--memory 8192 \
--disk path=/var/lib/libvirt/images/testvm.qcow2,size=40 \
--os-variant fedora-42 \
--cdrom ~/Downloads/Fedora-Workstation-Live-x86_64-42-1.6.iso \
--network network=default \
--graphics spice
4. NUMA-Aware CPU and Memory Configuration
NUMA configuration is not exposed by default in virt-manager, but you can define it manually in the domain XML.
Edit the VM definition:
sudo virsh edit testvm
Add NUMA topology inside <cpu> and <memoryBacking> sections:
<cpu mode='host-passthrough'>
<numa>
<cell id='0' cpus='0-3' memory='4096' unit='MiB'/>
<cell id='1' cpus='4-7' memory='4096' unit='MiB'/>
</numa>
</cpu>
<memoryBacking>
<source type='memfd'/>
<access mode='shared'/>
<allocation mode='immediate'/>
</memoryBacking>
This setup:
- Creates 2 NUMA nodes
- Assigns 4 vCPUs and 4 GB RAM per node
- Uses
memfdfor shared memory performance
Restart the VM:
sudo virsh destroy testvm
sudo virsh start testvm
Validate:
sudo virsh vcpuinfo testvm
sudo virsh dommemstat testvm
5. Advanced Customization Examples
Pin vCPUs to Physical Cores
sudo virsh vcpupin testvm 0 0
sudo virsh vcpupin testvm 1 1
sudo virsh vcpupin testvm 2 2
sudo virsh vcpupin testvm 3 3
Use Hugepages for Better Performance
Add to VM XML:
<memoryBacking>
<hugepages>
<page size='1' unit='GiB'/>
</hugepages>
</memoryBacking>
Enable hugepages on host:
sudo sysctl -w vm.nr_hugepages=8
6. Manage the VM
# Start / stop / autostart
sudo virsh start testvm
sudo virsh shutdown testvm
sudo virsh autostart testvm
# Connect to console
sudo virsh console testvm
Or use virt-manager GUI.
7. Optional: Bridge Networking
To connect your VM to the external LAN:
Fedora example
sudo nmcli connection add type bridge ifname br0 con-name br0
sudo nmcli connection add type ethernet ifname enp1s0 master br0
Then modify VM XML:
<interface type='bridge'>
<source bridge='br0'/>
<model type='virtio'/>
</interface>
8. Clone or Template VMs
sudo virt-clone --original testvm --name devvm --auto-clone
9. Delete VM
sudo virsh destroy testvm
sudo virsh undefine testvm --remove-all-storage
