The Verdict: Decision Tree for Pi 3 Routing

Using a Raspberry Pi 3 as a router is a classic edge-networking project, but it comes with a hard physical limitation: the USB 2.0 bus. The Pi 3 shares its 480 Mbps USB 2.0 bus between the four USB ports and the built-in Ethernet controller. This means your maximum real-world throughput is capped at roughly 300 Mbps, even if you plug in a Gigabit USB adapter. If you are routing a 1 Gbps fiber connection, the Pi 3 will bottleneck your network. If you are routing an IoT VLAN, a Pi-hole network, or a sub-300 Mbps WAN link, it is a highly capable, low-power router.

Your RequirementRecommended Hardware
WAN speed > 500 Mbps (Gigabit Fiber)Abandon Pi 3. Buy a Raspberry Pi 5 or a used enterprise mini-PC (e.g., Intel N100).
WAN speed < 300 Mbps + need low powerRaspberry Pi 3 Model B+ with an ASIX AX88179 USB adapter.
Need built-in dual-band WiFi routingAbandon Pi 3. The Pi 3 only has 2.4GHz 802.11n. Use a Pi 4 or Pi 5.

Default Pick for this Guide: We are building a wired edge router using the Raspberry Pi 3 Model B+ (which has better thermal management and a slightly improved Ethernet MAC than the older 3B) paired with an ASIX AX88179 USB-to-Gigabit Ethernet adapter.

Hardware Spec Sheet: Overcoming the USB Bottleneck

The most common reason Pi 3 router builds fail or drop packets is the use of cheap Realtek-based USB Ethernet adapters. The Realtek RTL8153 chipset is notorious for triggering USB UAS (USB Attached SCSI) bugs and dropping interrupts on the Pi 3's DWC2 USB controller under sustained load. You must use an adapter with the ASIX AX88179 chipset, which has native, stable mainline Linux kernel support.

ComponentExact Variant / ModelEstimated Cost (2026)
Compute BoardRaspberry Pi 3 Model B+ (1GB RAM)$35 - $45 (Used/Refurb)
WAN/LAN AdapterUGREEN or Cable Matters USB 3.0 to Gigabit Ethernet (Must specify AX88179 chipset)$16 - $20
StorageSanDisk Extreme 32GB A2 microSD (Raspberry Pi OS Lite)$12
Power SupplyOfficial Raspberry Pi 5.1V 2.5A Micro-USB Power Supply$12
Cabling2x CAT6 Patch Cables (WAN to Modem, LAN to Switch)$8
Power Warning: The Pi 3 B+ and a USB Ethernet adapter will draw close to 1.2A under load. Do not use generic phone chargers. If you see a yellow lightning bolt on the console (or read Under-voltage detected in dmesg), your USB adapter will randomly disconnect, killing your LAN routing.

Port Mapping and Logical Interface Assignment

Because the Pi 3 only has one physical RJ45 jack, we must map our physical ports to logical interfaces in the OS. Raspberry Pi OS uses predictable network interface names by default, but for a router, it is highly recommended to revert to standard ethX naming via raspi-config to make firewall scripting predictable.

Physical PortLogical InterfaceRoleIP Assignment
Built-in RJ45 Jacketh0WAN (Uplink to ISP Modem)DHCP Client (from ISP)
USB AX88179 Adaptereth1LAN (Downlink to Switch/PC)Static: 192.168.50.1/24
Built-in WiFi (Optional)wlan0Disabled or LAN BridgeN/A (Left unconfigured for stability)

Step-by-Step Router Configuration (nftables & dnsmasq)

Raspberry Pi OS (Bookworm and later) has deprecated iptables in favor of nftables. While iptables-legacy still works, writing native nftables rules is faster and future-proof. We will use dnsmasq for DHCP and DNS forwarding.

Target Board: Raspberry Pi 3 Model B+ running Raspberry Pi OS Lite (64-bit, Bookworm).

1. Prepare the OS and Interfaces

  1. Flash Raspberry Pi OS Lite (64-bit) to your microSD card.
  2. Boot the Pi, connect via SSH, and run sudo raspi-config.
  3. Navigate to Advanced Options > Network Interface Names and select No (this forces eth0 and eth1 instead of enx... MAC-based names).
  4. Reboot the Pi. Verify interfaces with ip link show. You should see eth0 and eth1.

2. Install Required Packages

sudo apt update
sudo apt install dnsmasq nftables -y
sudo systemctl enable nftables
sudo systemctl enable dnsmasq

3. Configure the LAN Interface

Edit /etc/dhcpcd.conf (or /etc/NetworkManager/system-connections/ if using NetworkManager, but dhcpcd is standard for Lite headless setups) to assign a static IP to the LAN port:

echo 'interface eth1
static ip_address=192.168.50.1/24' | sudo tee -a /etc/dhcpcd.conf
sudo systemctl restart dhcpcd

4. Configure dnsmasq (DHCP & DNS)

Backup the default config and write a clean router configuration:

sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
sudo nano /etc/dnsmasq.conf

Paste the following:

interface=eth1
dhcp-range=192.168.50.10,192.168.50.200,255.255.255.0,24h
dhcp-option=option:router,192.168.50.1
dhcp-option=option:dns-server,192.168.50.1
listen-address=::1,127.0.0.1,192.168.50.1
bind-interfaces

5. Enable IP Forwarding and NAT (The Router Script)

Create a script to enable kernel forwarding and load the nftables NAT rules. Save this as /usr/local/bin/setup-router.sh:

#!/bin/bash
# setup-router.sh - Raspberry Pi 3 Router NAT Configuration
# Target: Pi 3 B+ / Raspberry Pi OS Bookworm
set -e

WAN_IF='eth0'
LAN_IF='eth1'

# 1. Enable IPv4 forwarding in the kernel
echo 1 > /proc/sys/net/ipv4/ip_forward

# Make it persistent across reboots
if ! grep -q 'net.ipv4.ip_forward=1' /etc/sysctl.conf; then
    echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
fi

# 2. Flush existing nftables and apply NAT rules
nft flush ruleset

nft add table ip nat
nft add chain ip nat prerouting '{ type nat hook prerouting priority -100 ; policy accept ; }'
nft add chain ip nat postrouting '{ type nat hook postrouting priority 100 ; policy accept ; }'

# Masquerade outbound traffic from LAN to WAN
nft add rule ip nat postrouting oifname '$WAN_IF' masquerade

# Allow established/related connections back in
nft add table ip filter
nft add chain ip filter forward '{ type filter hook forward priority 0 ; policy drop ; }'
nft add rule ip filter forward iifname '$LAN_IF' oifname '$WAN_IF' accept
nft add rule ip filter forward iifname '$WAN_IF' oifname '$LAN_IF' ct state established,related accept

echo 'Router NAT and Forwarding rules applied successfully.'

Make it executable and run it:

sudo chmod +x /usr/local/bin/setup-router.sh
sudo /usr/local/bin/setup-router.sh
Persistence: To make the nftables rules survive a reboot, run sudo nft list ruleset > /etc/nftables.conf after executing the script, and ensure nftables.service is enabled.

Debugging: Exact Errors and the First Three Checks

When a Pi router fails to pass traffic or hand out IPs, it is almost always due to service conflicts or interface naming changes. Here are the exact error strings you will see and how to fix them.

Error 1: dnsmasq: failed to create listening socket for port 53: Address already in use

Ranked Causes:

  1. systemd-resolved is running: Modern Debian/Ubuntu-based systems use systemd-resolved as a local DNS stub listener on port 53. It conflicts with dnsmasq.
  2. Another DNS server is installed: Pi-hole or Unbound is already bound to port 53.

The Fix: Disable the stub listener in systemd-resolved, or stop it entirely if you don't need it on the router itself.

sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo systemctl restart dnsmasq

Error 2: nft: Error: Could not process rule: No such file or directory

Ranked Causes:

  1. Missing Table/Chain: You tried to add a rule to a chain (e.g., postrouting) before creating the parent table (e.g., nat) in the nft syntax.
  2. Typo in Interface Name: The script references eth1, but the OS assigned the USB adapter enx00e04c680123 because predictable interface names were not disabled in raspi-config.

The Fix: Run ip a. If your USB adapter is named enx..., either rename it via udev rules, disable predictable names in raspi-config, or update the LAN_IF variable in the bash script to match the exact enx string.

Error 3: LAN clients get IP addresses but cannot ping 8.8.8.8

Ranked Causes:

  1. IP Forwarding is off: The kernel is dropping packets between eth1 and eth0.
  2. WAN interface has no default route: eth0 didn't receive a DHCP lease from your ISP modem.

The Fix: Run cat /proc/sys/net/ipv4/ip_forward. If it returns 0, run echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward. Next, run ip route. If there is no default via... line pointing to eth0, restart dhcpcd on the WAN interface.

Extending or Simplifying the Build

Once your base router is passing traffic, you can tailor the build to your specific network needs.

How to Simplify: Convert to a Transparent Bridge

If you don't need NAT or DHCP and just want the Pi to act as a bridge (e.g., for inline network monitoring or passing a single public IP to a downstream device), strip out dnsmasq and nftables. Instead, use bridge-utils to bond eth0 and eth1 into a single br0 interface. This removes the CPU overhead of NAT, allowing the Pi 3 to pass traffic slightly faster, though still capped by the USB 2.0 bus.

How to Extend: Add Network-Wide Ad Blocking (Pi-hole)

Since dnsmasq is already handling DNS for your LAN, extending the build to block ads is trivial. Instead of installing the full Pi-hole suite (which installs its own dnsmasq and web server), simply append upstream blocklists directly to your existing dnsmasq config. Add conf-file=/etc/dnsmasq.d/blocklist.conf to your main config, download a standard hosts-file blocklist, and format it as address=/doubleclick.net/0.0.0.0. Restart dnsmasq, and your router now natively drops ad domains at the DNS level with zero extra memory overhead.

For deeper documentation on modern Debian networking, refer to the Raspberry Pi OS Configuration Guide and the Netfilter NAT Wiki. For advanced DNS routing options, consult the official dnsmasq manual.