A step-by-step guide to building a cluster-wide BGP EVPN overlay network on Proxmox VE —
distributed across every node — with automatic SNAT egress, a lightweight dnsmasq DHCP VM,
and strict lateral firewall isolation between your lab network and your home LAN.
Patching matters, but even a clean, low-dependency stack gets hit by zero-day logic flaws. When you run experimental workloads — unusual OS builds, bleeding-edge services, anything untrusted — your network layout should be the layer that stops an unexpected exploit cold before it reaches your core home infrastructure.
The usual homelab isolation tool is a pile of VLANs — and it works, until you want isolation plus dynamic routing across multiple hypervisors. The difference is which plane does the learning: VLANs learn in the data plane (flood-and-learn, hope for the best), while EVPN advertises reachability in the BGP control plane. Every Proxmox node running FRR is a BGP speaker in its own right — there's no central SDN appliance to install, and the fabric has no single point of failure to babysit.
That's what makes the network both decentralized and efficient: nodes only exchange what's actually needed to reach a destination, scoped per route target — not every packet for every guest, everywhere, forever.
| Property | Traditional VLANs | BGP EVPN Overlay (this build) |
|---|---|---|
| MAC/IP learning | Flood-and-learn in the data plane — unknown unicast hits every link, every node learns by listening to broadcasts | Advertised via MP-BGP over the control plane — known unicast never floods, ARP storms stay local |
| Scale ceiling | 4,094 VLAN IDs and one shared spanning-tree domain to babysit | 16.7M VNIs (24-bit VXLAN space) with a clean per-tenant VNet per lab |
| Adding a cluster node | More trunks, more native-VLAN landmines, more config that must match on every switch port | Add it to the controller's peer list — FRR turns the node into a BGP speaker and it learns the fabric on its own |
| Failure domain | One loop in one VLAN can storm the whole L2 island; STP parks links idle to avoid it | Routing boundaries in the fabric; no spanning tree in the overlay, failures contain themselves |
| Between subnets | Router-on-a-stick or a dedicated routing VM bolted onto every network | Anycast-style gateway (172.22.66.1) rides the fabric — every VM is one hop from its first-hop router no matter which node it lands on |
The stack below is a generic reference design you can adapt to any node count. Example values are used throughout — rename to fit your environment:
node1 (primary exit node), node2, node3 — resolvable on the home LAN (192.168.1.0/24, gateway 192.168.1.1).172.22.66.0/24 (gateway 172.22.66.1, SNAT enabled).evpncont, ASN 65001), one EVPN zone (evpnzone, VRF-backed VXLAN tag 10009), one VNet (vnetiso, VNI 100).172.22.66.2 running dnsmasq, attached directly to the VNet.iptables FORWARD rules on the exit node — internet egress allowed via SNAT, lateral home-LAN traffic dropped.
Install the FRR packages on all of your planned BGP EVPN Proxmox nodes — they handle the BGP protocol and make the EVPN controllers function:
apt update && apt install frr frr-pythontools libpve-network-perl
evpncont) with ASN 65001, listing your cluster peers (node1, node2, node3).evpnzone) linked to your controller, with VXLAN tag 10009.vnetiso, zone evpnzone, tag / VNI 100.172.22.66.0/24, gateway 172.22.66.1, SNAT enabled — this automatically handles outbound masquerading through the primary exit node.
To handle dynamic lease allocation on the isolated network, deploy a lightweight Alpine Linux VM attached
directly to vnetiso with a static IP (172.22.66.2) and configure dnsmasq:
interface=eth0 dhcp-range=172.22.66.100,172.22.66.200,255.255.255.0,12h dhcp-option=3,172.22.66.1 dhcp-option=6,1.1.1.1
Enable and start the service:
rc-update add dnsmasq default rc-service dnsmasq restart
VMs with a secondary network interface attached to vnetiso pull a DHCP lease natively via
ifupdown. Debian example (e.g. interface ens19) in /etc/network/interfaces:
# Secondary network interface on vnetiso allow-hotplug ens19 iface ens19 inet dhcp
sudo ifup ens19
Alpine guest equivalent (e.g. interface eth1):
# Secondary network interface on vnetiso auto eth1 iface eth1 inet dhcp
Note: don't just copy me — change the interface names depending on how the interface is detected by your VM. Also, remember that if you add the VNET as a secondary interface with your LAN, that makes the VM able to connect to both networks.
To keep the isolated 172.22.66.0/24 network from talking to other hosts on the home LAN
(192.168.1.0/24) while still reaching the gateway (192.168.1.1) and internet egress via SNAT,
apply these ordered forwarding rules on all cluster exit/fallback nodes:
# 1. Explicitly allow traffic to the gateway (must be evaluated FIRST) sudo iptables -I FORWARD 1 -s 172.22.66.0/24 -d 192.168.1.1 -j ACCEPT # 2. Drop all remaining traffic destined for the home LAN subnet sudo iptables -I FORWARD 2 -s 172.22.66.0/24 -d 192.168.1.0/24 -j DROP
Rule order is everything: the /32 gateway allow must land before the subnet-wide drop, or every VM loses DNS/API access and egress. To persist the rules across reboots:
sudo apt install iptables-persistent sudo netfilter-persistent save
From a VM inside vnetiso, run these tests and compare against the expected results:
| Test | Command | Expected | Why |
|---|---|---|---|
| Gateway ping | ping 172.22.66.1 |
SUCCESS | VNet gateway is local to the fabric |
| Home gateway ping | ping 192.168.1.1 |
SUCCESS | Allowed by the explicit gateway rule |
| Lateral LAN ping | ping 192.168.1.50 |
TIMEOUT | Dropped by the subnet DROP rule |
| Internet egress | ping 1.1.1.1 |
SUCCESS | Routed out via SNAT on the exit node |
If all four match, your isolated fabric is behaving exactly as designed: reachable, egress-capable, and hard-walled off from the home LAN.