If you want to run OpenWrt on a Raspberry Pi, the definitive choice is the Raspberry Pi 4 Model B (2GB RAM). It provides native Gigabit Ethernet, hardware NAT offloading support, and stable thermals without requiring an active cooling fan at routing loads. While the Pi 5 is faster, its PCIe architecture requires a specialized HAT for secondary network interfaces, and the Pi Zero 2 W lacks the physical throughput for modern broadband. This guide walks through the exact hardware build, interface mapping, a native GPIO watchdog script, and the specific error strings you will hit when configuring a Pi-based router.

The Hardware Decision Tree: Which Pi to Pick

Choosing the right board prevents bottlenecks. Here is the decision matrix for routing workloads, terminating in our default pick.

Board VariantNAT ThroughputThermals (Routing Load)Native PortsVerdict
Pi Zero 2 W~150 Mbps (USB2 bottleneck)Passive OK0x GbE, 1x USB2Reject: Too slow for WAN.
Pi 3 Model B+~300 Mbps (USB2 shared bus)Throttles without fan1x GbE (USB2 bus), 1x USB2Reject: Shared bus limits speed.
Pi 4 Model B (2GB)~940 Mbps (Native GbE)Passive OK (with case)1x Native GbE, 2x USB3DEFAULT PICK
Pi 5 (4GB)~1.5 Gbps (requires HAT)Requires Active Cooling1x Native GbE, 2x USB3Overkill unless using PCIe HAT.
Bench Note: The 2GB RAM variant of the Pi 4B is sufficient. OpenWrt's web interface (LuCI) and base routing services idle at roughly 120MB of RAM. Save your money and put it toward a high-quality USB3 Ethernet adapter.

Parts List & Interface/Pin Mapping

A Pi only has one physical Ethernet port. To build a standard router (one WAN, one LAN), you must add a secondary interface. Do not buy cheap USB2 adapters; they will cap your WAN at 300 Mbps. You need a USB3 Gigabit adapter with a chipset that has native OpenWrt kernel module support.

Exact Bill of Materials

  • Compute: Raspberry Pi 4 Model B (2GB RAM)
  • Enclosure: Flirc Raspberry Pi 4 Passive Aluminum Case (acts as a giant heatsink)
  • Storage: SanDisk Extreme 32GB microSD (A2 rating for better random I/O)
  • WAN Adapter: USB3 to Gigabit Ethernet Adapter (Must use Realtek RTL8156 chipset, e.g., UGREEN or Cable Matters RTL8156 models)
  • Power: Official Raspberry Pi 27W USB-C Power Supply

Interface & GPIO Mapping Table

Unlike Arduino projects where you map logic pins, in an OpenWrt Pi build, you map physical interfaces and sysfs GPIO pins to logical network zones.

Physical HardwareOpenWrt Logical InterfaceFirewall ZoneNotes
Pi 4 Native Ethernet (RJ45)eth0 (br-lan)LANConnects to your internal switch/APs.
USB3 RTL8156 Adaptereth1 (wan)WANConnects to ISP modem. Requires kmod-usb-net-rtl8156.
GPIO 21 (Pin 40) to GNDPhysical Reset ButtonN/AUsed by our watchdog script for factory reset.

Flashing and First Boot Configuration

OpenWrt provides two image types for the Pi: squashfs and ext4. You must use the ext4-factory image. The squashfs image relies on a specific partition layout that the Pi's Broadcom bootloader often struggles with when expanding the filesystem on larger SD cards.

  1. Download the Image: Go to the OpenWrt Table of Hardware and download the latest stable ext4-factory.img.gz for the BCM2711 (Pi 4).
  2. Flash the SD Card: Use BalenaEtcher or Raspberry Pi Imager. Do not use the Imager's 'OS customization' settings; let OpenWrt handle the network config.
  3. First Boot: Insert the SD card, connect the native Ethernet to your PC, and power on. The Pi will boot and assign itself 192.168.1.1.
  4. Install USB WAN Drivers: SSH into the Pi (ssh root@192.168.1.1) and install the driver for the RTL8156 adapter before configuring the network.
    opkg update
    opkg install kmod-usb-net-rtl8156
    reboot
  5. Configure Interfaces: In LuCI (the web UI), go to Network > Interfaces. Edit the WAN interface, set Protocol to 'DHCP client', and assign it to the new eth1 interface. Ensure the WAN interface is assigned to the wan firewall zone with Masquerading enabled.

GPIO Watchdog & Reset Script

Headless routers need a physical way to trigger a factory reset if you lock yourself out of LuCI. The following Bash script uses the native Linux sysfs GPIO interface. It targets the Pi 4B / Pi 400 (BCM2711 chip). It monitors GPIO 21 (Physical Pin 40). When pulled to ground via a momentary pushbutton for 3 seconds, it triggers an OpenWrt factory reset.

Save this as /usr/bin/gpio-reset.sh and make it executable (chmod +x), then add it to /etc/rc.local to run on boot.

#!/bin/bash
# Target: Raspberry Pi 4B / 400 (BCM2711)
# Pin: GPIO 21 (Physical Pin 40)

GPIO_PIN=21
GPIO_PATH="/sys/class/gpio/gpio${GPIO_PIN}"
HOLD_TIME=3 # Seconds to hold for reset

# Error handling: Clean up if GPIO is already exported from a crashed previous run
if [ -d "$GPIO_PATH" ]; then
    echo "$GPIO_PIN" > /sys/class/gpio/unexport
    sleep 1
fi

# Export the pin and configure
echo "$GPIO_PIN" > /sys/class/gpio/export
if [ $? -ne 0 ]; then
    logger -t gpio-reset "Failed to export GPIO $GPIO_PIN. Exiting."
    exit 1
fi

echo "in" > "$GPIO_PATH/direction"
echo "1" > "$GPIO_PATH/active_low" # Button pulls to GND, so active_low makes 1 = pressed

logger -t gpio-reset "Monitoring GPIO $GPIO_PIN for factory reset..."

# Polling loop with debounce
while true; do
    VALUE=$(cat "$GPIO_PATH/value")
    if [ "$VALUE" -eq 1 ]; then
        START_TIME=$(date +%s)
        # Check if button is held
        while [ "$VALUE" -eq 1 ]; do
            sleep 1
            CURRENT_TIME=$(date +%s)
            ELAPSED=$((CURRENT_TIME - START_TIME))
            if [ "$ELAPSED" -ge "$HOLD_TIME" ]; then
                logger -t gpio-reset "Button held for ${HOLD_TIME}s. Triggering firstboot!"
                /sbin/firstboot -y
                /sbin/reboot
                exit 0
            fi
            VALUE=$(cat "$GPIO_PATH/value")
        done
    fi
    sleep 0.5 # Polling interval
done
Wiring the Button: Connect one leg of a momentary tactile switch to Physical Pin 40 (GPIO 21) and the other leg to Physical Pin 39 (GND). Do not use a pull-up resistor; the script configures the internal pull-up via the sysfs direction and active_low parameters.

Debugging: Exact Error Strings & Ranked Causes

When building a Pi router, you will inevitably hit network state errors. Here are the exact strings logged in logread and how to fix them.

Error 1: br-lan: received packet on eth0 with own address as source address

What it means: The kernel is seeing its own MAC address coming back into the LAN bridge. This usually indicates a physical loop or a MAC cloning conflict.

  1. Most Likely Cause: You plugged the WAN cable and LAN cable into the same unmanaged switch, creating a physical bridge loop.
  2. Second Cause: Your ISP modem is locked to the MAC address of your old router, and you attempted to clone the MAC in OpenWrt, but the clone applied to the wrong interface (e.g., applied to br-lan instead of eth1).
  3. Fix: Verify physical cabling. If MAC cloning is required, go to Network > Interfaces > WAN > Advanced Settings, and enter the MAC address in the 'Override MAC address' field. Reboot the cable modem afterward.

Error 2: netifd: Interface 'wan' has link connectivity loss

What it means: The WAN interface dropped the physical link or failed to get a DHCP lease.

  1. Most Likely Cause: The USB Ethernet adapter went to sleep or crashed due to USB autosuspend. The RTL8156 chipset is notorious for this on Linux if not configured correctly.
  2. Second Cause: The ISP requires a specific MTU or VLAN tag (common with fiber ONTs) which is not set on the WAN interface.
  3. Fix: Disable USB autosuspend. Add usbcore.autosuspend=-1 to your /boot/cmdline.txt file. If on fiber, check if your ISP requires VLAN ID 201 or similar, and configure a VLAN interface over eth1 in LuCI.

The First Three Things to Check When WAN Fails

If you have no internet on the LAN side, run this mental checklist before wiping the SD card:

  1. Check Firewall Zones: Go to Network > Firewall. Ensure the wan interface is assigned to the wan zone, and that Masquerading (NAT) and MSS clamping are checked. If Masquerading is off, your LAN traffic reaches the ISP but gets dropped because it uses private IP space.
  2. Check Physical Interface Assignment: Go to Network > Interfaces > WAN > Physical Settings. Ensure it is strictly bound to eth1 (the USB adapter). If it is bridged with eth0, you have just merged your LAN and WAN into a single broadcast domain.
  3. Check DNS Forwarding: SSH in and run ping 8.8.8.8. If that works, but ping google.com fails, your issue is DNS. Go to Network > DHCP and DNS, and ensure 'Allow dynamic DNS updates' is checked, or manually set upstream DNS servers to 1.1.1.1.

Extending or Simplifying the Build

Not every deployment requires a dual-interface NAT router. Depending on your network topology, you should adapt the build.

How to Simplify: The 'Dumb AP' or VLAN-on-a-Stick

If you already have a primary router (like a UniFi Dream Machine) and just want the Pi to run AdGuard Home or act as a wireless controller, do not use the USB WAN adapter. Simplify the build by deleting the WAN interface in LuCI entirely. Change the LAN interface IP to match your main router's subnet, point its gateway to your main router, and disable the Pi's DHCP server. This turns the Pi into a transparent bridge, eliminating double-NAT headaches and reducing CPU load to near zero.

How to Extend: Dual-WAN Failover (mwan3)

If you are deploying this in a remote cabin with unreliable internet, you can extend the build to support dual-WAN. Purchase a second USB3 Ethernet adapter (or use a 4G LTE USB modem). Install the multi-WAN package:

opkg install luci-app-mwan3

Configure wan1 (RTL8156 Ethernet) and wan2 (LTE Modem) in LuCI. The mwan3 package will monitor interface health via ping checks and automatically route traffic through the LTE modem if the primary Ethernet link drops. Ensure you set the metric of the primary WAN lower (e.g., 10) than the backup WAN (e.g., 20) so traffic prefers the cheaper/faster link.

For a dedicated, always-on routing appliance, the Raspberry Pi 4B (2GB) running OpenWrt with an RTL8156 USB WAN adapter remains the most cost-effective, thermally stable, and thoroughly documented build available. Stick to the ext4 filesystem, bind your firewall zones strictly, and disable USB autosuspend to ensure months of uninterrupted uptime.