How to Run OpenWrt as a Virtual Router on Proxmox VE: VLAN Routing and Network Hardening

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Approach Comparison: Virtual Router Architectures

It was 2:15 AM on a Tuesday when my entire home rack went dark. Monitoring alerts flooded my phone from fifteen self-hosted nodes. DNS queries were timing out, and Ceph storage replication stalled.

Worse, my primary firewall VM had kernel-panicked during an off-site backup burst after chewing through 6 GB of RAM. Staring at an unresponsive console in the dark, I rethought my whole routing topology. Running a bloated virtual appliance at the edge of a homelab creates single points of failure you only discover during an outage.

When virtualizing network routing inside Proxmox VE, you generally choose between three approaches:

  • Heavyweight FreeBSD Firewalls (pfSense / OPNsense): Feature-rich security suites with deep packet inspection and large state tables. However, they typically need at least 2 dedicated vCPUs and 4 GB of RAM. That is unnecessary overhead if you only need fast inter-VLAN routing, DHCP, and NAT.
  • Physical Hardware Routers (MikroTik / Ubiquiti EdgeRouter): Clean separation between the hypervisor and the edge gateway. They stay up when the server reboots. The trade-off is fixed port layouts, slower CPU throughput on budget models, and no instant snapshot rollbacks when an experimental config breaks your network.
  • Lean Linux Virtual Routers (OpenWrt on KVM): Stripped of bloat and running on a modern Linux kernel. OpenWrt boots in roughly 3 seconds, idles under 70 MB of RAM, and talks directly to Proxmox Linux bridges. It handles standard Linux 802.1Q VLAN filtering without quirky BSD driver quirks.

Pros & Cons of Virtualizing OpenWrt on Proxmox

Placing your gateway on the same hardware that hosts test containers and storage pools introduces practical trade-offs.

The Advantages

  • Tiny Footprint: OpenWrt easily runs on 1 vCPU and 512 MB of RAM. On my Intel N100 node, it idles at 0.3% CPU usage while routing gigabit traffic.
  • Native Linux Bridge VLANs: OpenWrt handles dot1q tagging out of the box. Coupled with Proxmox’s vlan-aware bridge, trunking IoT, guest, and trusted subnets takes only a few lines in your config. You never have to touch an upstream switch port profile.
  • Fast Snapshot Rollbacks: If a firewall policy locks you out over SSH, you can restore a Proxmox snapshot in 10 seconds directly from the host CLI.
  • Wire-Speed VirtIO Throughput: Paravirtualized VirtIO drivers routinely push 940+ Mbps on standard 1 Gbps uplinks and over 9.2 Gbps on 10 Gbps virtual bridges with negligible CPU overhead, avoiding the need for dedicated PCIe pass-through.

The Drawbacks

  • Host Reboot Downtime: When the Proxmox host installs a new PVE kernel and reboots, your household loses Internet access for the 45 to 90 seconds it takes the machine to POST and boot VMs.
  • Management Lockout Traps: If you assign your Proxmox management IP to a subnet routed exclusively by the virtual OpenWrt instance, a bad firewall rule or broken bridge can cut off web GUI access to the host itself.

Recommended Setup for Resilient Homelab Routing

Never rely on a virtualized router interface to reach your Proxmox management IP. Instead, configure an independent management address on the underlying bridge.

Here is the architectural baseline I use on multi-port mini PCs and 1U servers:

  • WAN Bridge (vmbr1): Attached to your physical WAN NIC (e.g., enp2s0) linked to your modem or ONT. Keep this bridge completely unnumbered in Proxmox—no IP address or gateway configured on the host.
  • LAN & Trunk Bridge (vmbr0): Attached to your physical LAN NIC (e.g., enp1s0) with bridge-vlan-aware yes. Assign Proxmox a static host IP directly on this bridge (such as 192.168.1.2/24) so host access stays alive even if OpenWrt is shut down.
  • OpenWrt VM Specs: 1 vCPU (type host for AES-NI crypto), 512 MB RAM, a serial console, VirtIO NICs, and a 1 GB virtual disk.

Implementation Guide

Step 1: Configure VLAN-Aware Bridges on Proxmox

SSH into your Proxmox host and edit your network interfaces file:

nano /etc/network/interfaces

Set up dedicated bridges for your WAN interface and your internal trunk:

# Physical WAN interface (no host IP)
auto enp2s0
iface enp2s0 inet manual

# WAN Bridge passed to OpenWrt
auto vmbr1
iface vmbr1 inet manual
    bridge-ports enp2s0
    bridge-stp off
    bridge-fd 0

# Physical LAN interface
auto enp1s0
iface enp1s0 inet manual

# VLAN-Aware LAN Bridge (Proxmox management lives here)
auto vmbr0
iface vmbr0 inet static
    address 192.168.1.2/24
    gateway 192.168.1.1
    bridge-ports enp1s0
    bridge-stp off
    bridge-fd 0
    bridge-vlan-aware yes
    bridge-vids 2-4094

Apply the configuration cleanly without restarting the server:

ifreload -a

Step 2: Download and Extract the OpenWrt Image

OpenWrt does not provide a standard ISO wizard. Instead, grab the official x86/64 ext4 UEFI image directly onto the Proxmox host:

mkdir -p /root/openwrt-install && cd /root/openwrt-install

# Download the 23.05.4 generic x86/64 combined EFI image
wget https://downloads.openwrt.org/releases/23.05.4/targets/x86/64/openwrt-23.05.4-x86-64-generic-ext4-combined-efi.img.gz

# Extract the raw disk image
gunzip openwrt-23.05.4-x86-64-generic-ext4-combined-efi.img.gz

Step 3: Provision the Virtual Machine Shell

Create the VM using Proxmox’s qm CLI tool. We will assign VMID 100 and configure a virtual serial port for lightweight console access:

qm create 100 --name OpenWrt-Router \
  --bios ovmf \
  --machine q35 \
  --cpu host \
  --cores 1 \
  --memory 512 \
  --serial0 socket \
  --net0 virtio,bridge=vmbr0,firewall=0 \
  --net1 virtio,bridge=vmbr1,firewall=0 \
  --ostype l26

Import the downloaded image into your storage pool (e.g., local-lvm) and expand the disk beyond the stock 120 MB size:

# Import image to storage pool
qm importdisk 100 openwrt-23.05.4-x86-64-generic-ext4-combined-efi.img local-lvm

# Attach disk on the VirtIO SCSI controller
qm set 100 --scsihw virtio-scsi-single --scsi0 local-lvm:vm-100-disk-0

# Add an EFI disk for UEFI boot variables
qm set 100 --efidisk0 local-lvm:0,format=raw,efitype=4m,pre-enrolled-keys=0

# Configure boot order and expand virtual disk to 1GB
qm set 100 --boot order=scsi0
qm resize 100 scsi0 +880M

Step 4: Initial Network & LuCI Web Interface Configuration

Boot the VM and connect directly to its serial console:

qm start 100
qm terminal 100

Hit Enter to open the OpenWrt root prompt. In this setup, eth0 connects to vmbr0 (LAN) and eth1 connects to vmbr1 (WAN). Configure the static LAN gateway address:

uci set network.lan.ipaddr='192.168.1.1'
uci set network.lan.netmask='255.255.255.0'
uci commit network
/etc/init.d/network restart

Once your WAN interface acquires an IP via DHCP, install the SQM package and the CAKE queue scheduler module:

opkg update
opkg install luci-app-sqm kmod-sched-cake

Step 5: Configure VLAN Segments and Firewall Zones

Next, isolate untrusted smart devices on VLAN ID 20. Rather than creating extra virtual network cards in Proxmox, attach a tagged subinterface (eth0.20) directly in OpenWrt.

Define the new interface and DHCP scope in /etc/config/network and /etc/config/dhcp:

# Set up the IoT network on VLAN 20
uci set network.iot=interface
uci set network.iot.proto='static'
uci set network.iot.device='eth0.20'
uci set network.iot.ipaddr='192.168.20.1'
uci set network.iot.netmask='255.255.255.0'

# Configure DHCP pool (192.168.20.100 - 192.168.20.250)
uci set dhcp.iot=dhcp
uci set dhcp.iot.interface='iot'
uci set dhcp.iot.start='100'
uci set dhcp.iot.limit='150'
uci set dhcp.iot.leasetime='12h'

uci commit network
uci commit dhcp

Lock down the IoT zone in /etc/config/firewall. The policy below permits outbound Internet access while blocking connections into your private LAN and hypervisor subnet:

# Create restricted IoT firewall zone
uci add firewall zone
uci set firewall.@zone[-1].name='iot'
uci set firewall.@zone[-1].network='iot'
uci set firewall.@zone[-1].input='REJECT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].forward='REJECT'

# Allow IoT devices outbound WAN routing
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='iot'
uci set firewall.@forwarding[-1].dest='wan'

# Allow local DNS queries (port 53)
uci add firewall rule
uci set firewall.@rule[-1].name='Allow-IoT-DNS'
uci set firewall.@rule[-1].src='iot'
uci set firewall.@rule[-1].proto='tcpudp'
uci set firewall.@rule[-1].dest_port='53'
uci set firewall.@rule[-1].target='ACCEPT'

# Allow local DHCP requests (port 67-68)
uci add firewall rule
uci set firewall.@rule[-1].name='Allow-IoT-DHCP'
uci set firewall.@rule[-1].src='iot'
uci set firewall.@rule[-1].proto='udp'
uci set firewall.@rule[-1].dest_port='67-68'
uci set firewall.@rule[-1].target='ACCEPT'

uci commit firewall
/etc/init.d/firewall restart

Step 6: Eliminate Bufferbloat with CAKE SQM

Saturating a symmetric or asymmetric uplink causes severe latency spikes during heavy transfers. On a typical 1 Gbps / 50 Mbps cable connection, unmanaged queues can push ping times from 15 ms to over 350 ms under load.

To eliminate this bufferbloat, apply CAKE shaping to your WAN interface (eth1). Set the bandwidth limits to roughly 90–95% of your measured baseline:

uci set sqm.eth1=queue
uci set sqm.eth1.interface='eth1'
uci set sqm.eth1.qdisc='cake'
uci set sqm.eth1.script='layer_cake.qos'
uci set sqm.eth1.enabled='1'
# Bandwidth set in kbit/s (e.g. 940 Mbps down, 48 Mbps up)
uci set sqm.eth1.download='940000'
uci set sqm.eth1.upload='48000'
uci commit sqm
/etc/init.d/sqm restart

After applying these settings, run a test on Waveform Bufferbloat Test. Ping jitter under 100% upload saturation typically drops from 200+ ms down to less than 3 ms. You now have an isolated, snapshot-capable router drawing under 2 watts of equivalent CPU power on your host.

Share: