Server

libvirt Networking: NAT vs Bridged vs Others

By Prabath Thalangama· October 24, 2026· 4 min read
#libvirt#kvm#networking

Introduction

libvirt offers several ways to connect a VM's virtual NIC to the outside world. The choice comes down to one question: do other machines need to reach the VM directly?

NAT (the default default network)

<network>
  <name>default</name>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp><range start='192.168.122.2' end='192.168.122.254'/></dhcp>
  </ip>
</network>
  • libvirt creates virbr0, runs dnsmasq on it for DHCP + DNS, and adds iptables/nftables MASQUERADE rules.
  • VMs get 192.168.122.x, reach the internet via the host's IP.
  • Nothing outside the host can initiate a connection to the VM (without port forwarding).
  • Great for: dev laptops, throwaway VMs, anything that only makes outbound connections.

Port-forward a service in with a hook or virsh-managed rule if you must, but that's a sign you want bridged.

Bridged (VMs on the LAN)

The VM's NIC joins a Linux bridge that also contains the host's physical NIC. The VM is a first-class citizen on your LAN — it gets an IP from the LAN's DHCP, and anything on the LAN can reach it.

Create the bridge (persistently — do it on the console, you'll drop SSH):

# NetworkManager (RHEL/Ubuntu)
nmcli con add type bridge con-name br0 ifname br0
nmcli con add type ethernet con-name br0-port ifname eth0 master br0
nmcli con mod br0 ipv4.method manual ipv4.addresses 192.168.1.10/24 \
  ipv4.gateway 192.168.1.1 ipv4.dns 192.168.1.1
nmcli con up br0

Then a libvirt network (or just reference the bridge directly):

<network>
  <name>lan</name>
  <forward mode='bridge'/>
  <bridge name='br0'/>
</network>
<interface type='bridge'>
  <source bridge='br0'/>
  <model type='virtio'/>
</interface>
  • Use for: servers, anything that needs a stable reachable address, VMs that peers connect to.
  • Caveat: Wi-Fi usually can't be bridged (APs drop frames with a MAC other than the associated one) — use routed mode or macvtap on wireless.

Routed

<forward mode='route'/>

Like NAT but without the address translation — the host routes between the VM subnet and the LAN. The VM's real (private) subnet IP is visible, but the rest of the LAN needs a route to that subnet pointing at the host (usually a static route on the router). Niche; use when you want isolation of the VM subnet but full reachability.

Isolated

<network><name>isolated</name><bridge name='virbr1'/>
  <ip address='10.10.0.1' netmask='255.255.255.0'>
    <dhcp><range start='10.10.0.2' end='10.10.0.254'/></dhcp>
  </ip>
</network>

No <forward> — VMs on this network talk to each other and the host, but have no route out. For lab environments, a DB tier that shouldn't face the internet, etc.

macvtap

Attaches the VM directly to a physical NIC with its own MAC, bypassing a bridge:

<interface type='direct'>
  <source dev='eth0' mode='bridge'/>
  <model type='virtio'/>
</interface>

Slightly lower overhead than a bridge. Big gotcha: in the default bridge (vepa-ish) mode, the VM cannot talk to its own host — the traffic goes out the NIC and the host's stack never sees it. Fine if the host is purely a hypervisor; annoying otherwise.

Verification and troubleshooting

virsh net-list --all
virsh net-dumpxml default
brctl show    # or: bridge link
ip -br addr
virsh domifaddr myvm       # VM's leased IP (NAT/managed networks)
  • VM has no network — the libvirt network isn't started (virsh net-start default), or (bridge) the bridge doesn't exist / the physical NIC isn't enslaved (bridge link).
  • NAT VM can't reach internet — host IP forwarding off (sysctl net.ipv4.ip_forward → set 1), firewalld/nftables blocking the forward chain, or a firewalld zone change removed libvirt's rules (systemctl restart libvirtd).
  • Bridged VM gets no DHCP — the LAN DHCP isn't reaching it: the host bridge has STP delay (set stp off or forward-delay 0), or a switch port with port-security / one-MAC-per-port.
  • Bridged VM works but host↔VM fails — you used macvtap, not a real bridge. Switch to a Linux bridge, or add a macvtap interface on the host too.
  • Wi-Fi + bridge = nothing works — expected. Use routed mode or macvtap with mode='bridge' on the wireless NIC (and accept host↔VM limitation), or NAT.
  • virbr0 still there after deleting the network — virsh net-undefine default and it's gone on next libvirtd restart; the interface lingers until then.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.