Install the stack

sudo pacman -S qemu-desktop virt-manager gnome-boxes dnsmasq iptables-nft libvirt edk2-ovmf

The edk2-ovmf package gives you UEFI firmware images for guest VMs; without it, only BIOS boots work. iptables-nft is the nftables-compatible iptables frontend that modern libvirt expects (the original iptables AUR package pulls in the legacy binary; don't mix them).

Enable libvirtd

sudo systemctl enable --now libvirtd.service
sudo systemctl status libvirtd.service

Let your user manage libvirt without sudo

Drop in a polkit rule so members of the libvirt group can manage VMs:

sudo tee /etc/polkit-1/rules.d/50-libvirt.rules <<'EOF'
polkit.addRule(function(action, subject) {
    if (action.id == "org.libvirt.unix.manage" &&
        subject.isInGroup("libvirt")) {
            return polkit.Result.YES;
    }
});
EOF

sudo groupadd --system libvirt 2>/dev/null || true
sudo gpasswd -a "$USER" libvirt
# log out and back in so the group membership applies

Alternatively, most distros ship a pre-existing kvm group — use that if it's the one your kernel has already granted /dev/kvm access to (check with ls -l /dev/kvm).

Bring up the default libvirt network

This is the NAT-based network that virt-manager uses out of the box. If it isn't already running:

sudo virsh net-list --all
# If the 'default' network isn't listed:
sudo virsh net-define /usr/share/libvirt/networks/default.xml
# Start it and set it to autostart:
sudo virsh net-start default
sudo virsh net-autostart default

Confirm you now have a virbr0 interface:

ip addr show virbr0

You should see 192.168.122.1/24 (the default libvirt subnet). Guests attached to this network get an IP via libvirt's embedded dnsmasq and can reach the internet through NAT.

Enable the QEMU bridge helper (for non-root bridging)

This allows unprivileged QEMU processes to attach a tap device to your bridge — important for gnome-boxes, which doesn't use libvirt and can't use privileged bridging:

sudo mkdir -p /etc/qemu
sudo tee /etc/qemu/bridge.conf <<'EOF'
allow virbr0
EOF
sudo chmod 644 /etc/qemu/bridge.conf

# Make qemu-bridge-helper setuid so unprivileged users can invoke it
sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper
Setuid-root is a trade-off

The bridge-helper only needs CAP_NET_ADMIN, but chmod u+s makes it fully setuid-root. On a personal desktop this is fine. In a multi-user environment consider granting the capability instead: sudo setcap cap_net_admin+ep /usr/lib/qemu/qemu-bridge-helper.

For a real bridge to your LAN (not libvirt NAT)

If you want the VMs to get addresses directly from your home/office router rather than libvirt's subnet, the extra step is creating a Linux bridge that slaves your physical NIC:

# With systemd-networkd:
sudo tee /etc/systemd/network/10-br0.netdev <<'EOF'
[NetDev]
Name=br0
Kind=bridge
EOF

sudo tee /etc/systemd/network/20-br0-bind.network <<'EOF'
[Match]
Name=enp3s0          # your actual NIC

[Network]
Bridge=br0
EOF

sudo tee /etc/systemd/network/30-br0.network <<'EOF'
[Match]
Name=br0

[Network]
DHCP=yes
EOF

sudo systemctl enable --now systemd-networkd

In virt-manager, edit a VM → NIC → set network source to "Bridge device..." and name br0. The VM now lives on your LAN directly.

Verify

  • sudo virsh net-list --all — the default network should be active and autostart.
  • ip link show type bridge — at least virbr0, plus br0 if you set one up.
  • Inside a guest: ip route get 1.1.1.1 — should return via the bridge IP.
  • From the host: ping <guest-ip> — should work now that host and guest share a subnet.