If you’re anything like me, you value privacy, encrypted connection and you absolutely hate ads. Finding a router that delivers on those “premium” features can be either absurdly expensive or simply impossible. So let’s build our own—at roughly the cost of a standard home router—with everything we want.

A quick caveat: this guide covers a basic “one in, one out” setup—just a single WAN port and a single LAN port on the Pi. If you need wireless, extra LAN ports, or more advanced features, you can pair your Pi router with a low-cost secondary router or switch (many of which also support OpenWRT). But today, we’ll focus on the core: transforming a Raspberry Pi into a privacy-oriented, ad-blocking router.

1. Getting the Hardware

Before diving into software, make sure you have the right components:

  • Raspberry Pi 4 or 5 (or similar): The Pi 4/5 have far more CPU power and RAM than typical routers, which is great for running services like AdGuard Home. If your internet speed is ≤ 100 Mbps, even an older Pi model (e.g. Pi 3B+) can suffice. I chose a Pi 4 because I had one on hand. Note that these Pi models each have only one built-in Ethernet port, so you’ll need a second network interface to handle both WAN (internet) and LAN (home network). Typically this is a USB Ethernet adapter. Alternatively, you could use a board with two Ethernet ports (for example, the Orange Pi R1 LTS) to avoid the USB adapter.
  • USB Ethernet Adapter: This will act as the second network interface (either WAN or LAN, depending on your setup). A popular choice is the TP-Link UE300 USB 3.0 Gigabit adapter with a Realtek RTL8153 chipset, which is well-supported by OpenWRT. (If your internet is only 100 Mbps, you could use an older Pi with a built-in 100 Mbps port or a cheaper USB adapter, but sticking with Gigabit hardware on a Pi 4/5 future-proofs your setup.)
  • MicroSD Card (8 GB or larger): This holds the OpenWRT system. I recommend 8 GB, which will be plenty even with many packages and logs. We will expand the filesystem later to use the entire card.
  • Ethernet Cables: At least two cables – one to connect the Pi’s Ethernet to your modem (WAN), and one to connect the Pi (LAN) to a switch or directly to a PC.
  • (Optional) Case and Cooling: The Pi will run 24/7, possibly under heavy load. A ventilated case, a small fan, or heatsinks can help keep temperatures down. This isn’t strictly required, but it can improve stability and prolong the Pi’s life, especially if you push it with sustained Gigabit traffic or DNS filtering.

2. Creating an OpenWRT Image

The OpenWRT Firmware Selector website simplifies getting the right image for your Pi. You can choose your exact board and even add packages (like USB Ethernet drivers) before downloading, which saves headaches later.

Steps to create a custom OpenWRT image:

  • Open the Firmware Selector: Go to the OpenWRT Firmware Selector website. In the search box, type your device model (e.g. “Raspberry Pi 4”) and select the matching target.

  • Choose the OpenWRT version: Pick the latest stable release unless you have a specific reason to use a snapshot. At the time of writing, OpenWRT 24.10.1 is the current stable release. The stable builds include the LuCI web interface by default, which is very convenient. (Snapshots generally do not include LuCI, so only use them if you need cutting-edge features and are comfortable installing LuCI separately.)

  • Select image type: You’ll typically see options for SquashFS vs. Ext4 images. For ease of use, select the Ext4 image. This format allows easy resizing of the filesystem to fill your SD card (we’ll expand it soon). (By contrast, SquashFS is read-only with an overlay – it’s more space-efficient for routers with tiny flash, but on a spacious SD card an Ext4 image is simpler to manage.)

  • Include USB Ethernet driver packages: This is crucial. By default, the image might not have the driver for your USB NIC, and then networking won’t work out of the box. On the Firmware Selector page, click Customize or Advanced options to add packages. Include the kernel modules for your adapter’s chipset, for example:

    • Realtek (RTL8152/RTL8153): Add kmod-usb-net-rtl8152. This covers many common USB 3.0 Gigabit adapters (e.g. TP-Link UE300).
    • ASIX (AX88179/AX88772): Add kmod-usb-net-asix-ax88179 if your adapter uses an ASIX chipset (common in some Cable Matters, Plugable, and other USB3 Gigabit adapters).

    Also ensure the basic USB networking support packages are present: kmod-usb-net and kmod-mii. (The Firmware Selector might auto-add these when you pick the specific driver, but double-check.)

  • *LuCI (if using a snapshot): If you chose a snapshot or it didn’t include LuCI, add luci, luci-base, luci-mod-admin-full, uhttpd, and luci-theme-bootstrap. This will give you the web interface on first boot. (If you picked a stable image, LuCI is already included, so you don’t need to add it.)*

  • Download the image: After customizing, click Download. You’ll get a compressed image file (usually around 30–50 MB). Save it to your computer and note where you put it.

  • Verify checksum (optional but recommended): On the download page, OpenWRT provides a SHA256 checksum. You can verify the image’s integrity (for example, run sha256sum <filename> on Linux/Mac, or use certutil -hashfile <filename> sha256 on Windows) and compare it to the given value. This ensures the file downloaded correctly and wasn’t tampered with. (This step is good practice, though many skip it for convenience.)

Now you have a tailored OpenWRT image that includes support for your Pi and USB Ethernet adapter. Next, we’ll flash it to the SD card.

3. Flashing Image to the SD Card

Flashing the image is straightforward. You can use any standard Raspberry Pi imaging tool. For example, the Raspberry Pi Imager works well. Other options include Balena Etcher or the dd command on Linux. Simply select the downloaded OpenWRT file and write it to your MicroSD card. Once the tool finishes, you should have a bootable SD card with OpenWRT.

4. Expanding the Filesystem

By default, the OpenWRT image uses a very small partition for its root filesystem (just enough for the OS). If your SD card is larger (16 GB, 32 GB, etc.), most of the space will be unused. We’ll expand the root partition to fill the card so we can use the extra space for installing packages, storing logs, and keeping more DNS data.

Why this matters: OpenWRT targets many routers that have very limited flash (like 128 MB). On a Raspberry Pi, the “flash” is the SD card, which is typically much larger. OpenWRT doesn’t automatically expand to fill the whole card, so we must do it manually. If we skip this step, the router will only see the small default partition (maybe ~256 MB or so), and the rest of the SD card will be wasted.

Here’s how to expand the partition:

  • Use GParted: I did this from a Linux machine. You can boot a Linux live USB if your main OS is Windows. Insert the SD card into the PC.
  • Open GParted and select the SD card device: You will see two partitions: a small FAT32 boot partition (~50 MB) and a larger ext4 partition (rootfs) of a few hundred MB, followed by a lot of unallocated space.
  • Unmount partitions if needed: Right-click on each partition and choose “Unmount” (if GParted shows they are mounted).
  • Resize the rootfs partition: Right-click the ext4 (rootfs) partition, choose “Resize/Move”. In the dialog, drag the slider or enter a new size to extend it to the maximum available space on the card (you can leave a few megabytes at the end for alignment if GParted suggests it). Click “Resize/Move”.
  • Apply the operation: Click the checkmark or Apply button in GParted. It should quickly resize the ext4 partition to fill the card.

After expanding, the ext4 partition now occupies nearly the entire SD card (minus the boot partition).

Updating PARTUUID: Because OpenWRT’s newer Pi images use PARTUUID in cmdline.txt, resizing changes the partition UUID. We must update the references so the Pi boots correctly.

  • Reinsert or remount the SD card if needed.
  • Run sudo blkid in a terminal to find the new PARTUUID of the resized ext4 partition (it will look like a string such as e2d6f960-02).
  • Mount or browse to the boot partition (the small FAT32 one). Open the file cmdline.txt. In that file, find the root=PARTUUID=xxxxx parameter. Replace the old PARTUUID with the new one you got from blkid.
  • If there’s a file named partuuid.txt on the boot partition, open it and update the first line to the new PARTUUID (dropping any trailing -0x if present).
  • Save the files and unmount/eject the SD card.

Now the SD card’s partitions are fully sized, and the bootloader knows the new root partition ID. We’re ready to boot the Pi with OpenWRT.

5. Getting the Raspberry Pi Online

Time for the exciting part: booting OpenWRT on the Pi and getting the network interfaces configured so it can actually route traffic. The goal is to have your Pi router serving DHCP to your LAN devices and providing internet access through its WAN port.

  • First Boot & LAN Access: Insert the SD card into the Pi. Connect the Pi’s built-in Ethernet port to your PC or to a switch on your PC’s network. Don’t yet connect the USB Ethernet adapter to the modem (ISP). Power on the Pi. It will boot OpenWRT; give it about a minute. (The first boot may take a bit longer as it initializes the filesystem, but usually it’s under 30 seconds on a Pi.)
  • Access the Web Interface: By default, OpenWRT’s LAN side uses IP 192.168.1.1/24 and runs a DHCP server. Your PC should automatically get an address like 192.168.1.100. If it doesn’t, temporarily set a static IP on your PC (e.g. 192.168.1.10/24 with gateway 192.168.1.1). Then open a web browser and go to You should see the LuCI login page. (On a fresh stable build, there is no password set yet, so it should let you in immediately or prompt you to set one.)

If you used a snapshot image without LuCI, you won’t have the web interface yet. In that case, use SSH instead: connect to root@192.168.1.1 (no password). Then you would install LuCI later. Assuming you used the stable image or added LuCI, you can proceed in the web UI.

  • Set a root password: OpenWRT by default has no password (for LAN). LuCI should prompt you to set one. Choose a strong password. If you’re on SSH, run passwd and enter a password. This secures the router so others can’t just log in.

  • Limit SSH Access to LAN Only: To ensure SSH access is only available from your local network, go to System > Administration > SSH Access in LuCI. Under the "Interface" setting, select LAN. This restricts the Dropbear SSH server to listen only on the LAN interface, preventing any access attempts from the WAN or other interfaces. This is a simple but essential step to enhance security.

  • Verify network interfaces: In LuCI, go to Network > Interfaces. You should see at least the default LAN (which is a bridge, usually br-lan using eth0). If your image included the USB NIC drivers, you should also see a new physical interface (likely eth1) listed (it might appear under the “New Device” dropdown when adding an interface). If you don’t see the USB adapter at all, check Status > Kernel Log or System Log to see if it was recognized. If not, it may mean the driver package didn’t match your adapter; you’d then need to install the correct one manually with an .ipk package and reboot. (This is why we included the driver in the image.)

  • Avoid IP Conflicts (via SSH): If your ISP’s modem/router (upstream device) already uses the 192.168.1.x subnet (as many do), it’s safest to put OpenWRT on a different network to prevent address clashes. You can skip this step if this Pi is your only router and you’re not double-NAT’ing but I suggest you to do this because using a private range like 10.x.x.x gives you extra separation from the common 192.168.x.x space. To do this just SSH into your Pi router (still on the old LAN IP) and run:

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

    Then:

    reboot
    

    This moves your LAN to 10.0.0.1/24 (or whatever subnet you prefer).

    • Reconfigure your PC: If you use a static address, switch to something like 10.0.0.10/24 with gateway 10.0.0.1. Or simply reconnect via DHCP—your PC should now get an IP in the new range.
    • Reconnect to LuCI/SSH: Point your browser or SSH client to 10.0.0.1 (or the subnet you chose). You’ll be back in a network that won’t conflict with your ISP’s router.
  • Configure the WAN interface (USB Ethernet): Now let’s set up the Pi’s WAN so it can get an internet connection. In LuCI, go to Network > Interfaces, click Add new interface if a “wan” isn’t already listed. Name it “WAN”. For protocol, choose DHCP client (the most common case). In the Device dropdown, select the USB Ethernet interface (likely named eth1). If a wan entry exists but is unconfigured, edit it and set Protocol = DHCP and Interface = eth1.

    In the same interface settings, go to the Firewall Settings tab and ensure this interface is assigned to the WAN firewall zone (the default zone for WAN). By default, OpenWRT’s WAN zone is set to reject unsolicited inbound traffic and to masquerade outbound (NAT). That’s usually fine. Click Save & Apply.

    The Pi will now try to get an IP on eth1. After a moment, check Status > Interfaces. The WAN interface should have an IP from your ISP or upstream router (e.g., something like 192.168.1.x if behind another router, or a public IP if directly connected).

    • If you use PPPoE or a static IP from your ISP (common with DSL/fiber), choose PPPoE or Static instead of DHCP and fill in your ISP credentials or addresses. For example, if using PPPoE with VLAN tagging, you might set the device to eth1.35 to use VLAN 35 on eth1.
  • Test internet connectivity: Now physically connect the USB Ethernet adapter (WAN) to your modem/router. After a minute, the Pi should obtain internet access. In LuCI, go to Status > Overview (or Status > Interfaces) to see if the WAN has an IP and (for DHCP) a gateway.

    To test from the router, open Status > Diagnostics in LuCI or SSH into the Pi. Try pinging an external IP:

    ping -c 4 8.8.8.8   # Google DNS
    

    If that succeeds, try a DNS name:

    ping -c 4 google.com
    

    If both pings work, your Pi router has internet! At this point, any PC on the LAN (including the one you’re using) should be able to browse the web through the Pi.

With this, your Pi is now functioning as a basic router: its LAN side is giving DHCP to clients, and it routes traffic to the WAN/ISP. Next, we’ll install AdGuard Home on the router to block ads for all devices.

6. Installing AdGuard Home

AdGuard Home (AGH) is a network-wide DNS ad blocker and privacy tool. Installing it on OpenWRT lets you filter ads and trackers at the router level, without needing individual browser add-ons. We’ll use OpenWRT’s package system (opkg) to install it easily.

  • Update package lists: In LuCI, go to System > Software, click Update lists. (Or SSH into the router and run opkg update.) This refreshes the list of available packages.

  • Install the AdGuard Home package: Still in System > Software, search for “adguardhome”. You should see an adguardhome package. Click Install. (Or via SSH, run opkg install adguardhome.) The package will download and install.

  • Enable and start AdGuard Home: After installation, ensure the service is enabled to run on boot, and start it now:

    /etc/init.d/adguardhome enable
    /etc/init.d/adguardhome start
    

    (In LuCI you might also see a way to enable/disable services under System > Startup.)

  • Initial setup wizard: By default, on first run AdGuard Home listens on port 3000 for setup. Open a browser on a PC and go to http://<router_LAN_IP>:3000 (e.g., http://192.168.2.1:3000 if you didn’t change the LAN IP). You should see the AdGuard Home welcome screen.

  • Admin Web Interface: The wizard will prompt for which IP and port the AdGuard UI should bind to. Enter the router’s LAN IP (e.g. on interface br-lan) and choose a port (I recommend 8080). We avoid port 80 because LuCI’s web UI is already on 80, and 3000 is just for setup. (You can later use HTTPS on 443 if desired, but for now 8080 is fine.)

  • DNS Server Listener: Next, set the DNS listening IP/port. Again use the LAN IP (br-lan) and pick a port. I suggest 5353 for AdGuard’s DNS. Port 53 is the standard DNS port, but OpenWRT’s own dnsmasq is still on 53. By using 5353, we allow both to coexist. (Later we’ll forward queries from 53 to AdGuard’s 5353.)

  • Credentials: Choose a username and password for the AdGuard Home UI (this is separate from the router’s root password). Finish the wizard and save.

  • Access the AdGuard Home dashboard: Now you can reach AdGuard Home’s interface at http://<router_LAN_IP>:8080 (e.g. http://192.168.2.1:8080). Log in with the credentials you just set. At this point, AdGuard Home is running, but it isn’t yet handling any DNS queries from your devices (we’ll change that next).

7. Configuring DNS to Use AGH

At this point, network clients are still using OpenWRT’s built-in DNS (dnsmasq). We want all DNS queries from your LAN clients to go through AdGuard Home, so it can filter ads. We’ll configure OpenWRT to forward DNS requests to AdGuard, and then set AdGuard’s upstream servers.

  • Forward DNS queries to AdGuard: In LuCI, go to Network > DHCP and DNS > DNS Forwardings (this edits /etc/config/dhcp). Add a forwarding entry like 192.168.2.1#5353. (Replace 192.168.2.1 with your router’s LAN IP if different, and 5353 with the port you set AdGuard’s DNS to listen on.) This tells dnsmasq to forward all DNS queries it receives to AdGuard Home on port 5353. Also go to Resolv & Hosts Files section check the box “Ignore /etc/resolv.conf” (sometimes shown as “Ignore resolve file”). This ensures OpenWRT’s dnsmasq won’t send queries to the ISP’s DNS directly; we want everything to go through AdGuard. Click Save & Apply.

Now, any device on the LAN that uses the router (192.168.2.1) for DNS will have its queries forwarded to AdGuard Home.

(Optional alternative: You could disable dnsmasq’s DNS server entirely by setting its “DNS server port” to 0, and then change AdGuard to listen on port 53. This effectively hands DNS on port 53 entirely to AdGuard. However, then you’d need to ensure local hostnames are resolved properly by AdGuard or other means. Our forwarding method keeps things simpler and retains local DNS via dnsmasq.)*

  • Configure AdGuard Home upstream DNS: In the AdGuard Home web UI, go to Settings > DNS Settings. Here you choose your upstream DNS servers (the ones AdGuard uses for queries not blocked). You can use popular public DNS like Cloudflare (1.1.1.1), Google (8.8.8.8), Quad9 (9.9.9.9), etc. AdGuard also lets you use encrypted DNS (DoH, DoT, DoQ) if you prefer privacy. For example, you might add Cloudflare and Google DNS over HTTPS. I set two upstreams for redundancy. The key is to choose fast, reliable servers that you trust. AdGuard Home supports DNS-over-HTTPS (DoH), DNS-over-TLS (DoT), and DNS-over-QUIC (DoQ) if you want encrypted queries upstream.

  • Intercept DNS (Force All Clients Through AdGuard): Some devices or apps ignore the DHCP-provided DNS servers and hardcode public resolvers (e.g., 8.8.8.8). To ensure all DNS traffic is filtered by AdGuard Home, you can create a firewall redirect that transparently forwards any outbound DNS request to AdGuard’s listener:

    In LuCI Go to Network > Firewall > Port Forwards (or Traffic Rules). Click Add (or New Rule) and configure:

    Name: Intercept-DNS
    Protocol: TCP + UDP
    Source zone: lan
    Source port: 53
    Destination zone: (leave blank or choose lan)
    Destination IP address: 192.168.2.1 (your router’s LAN IP)
    Destination port: 5353 (the port AdGuard Home listens on)
    

    Click Save and Apply. Now, whenever a client tries to reach any DNS server on port 53, the router will intercept and redirect the query to 192.168.2.1:5353, ensuring every DNS lookup goes through AdGuard Home.

At this stage, all DNS requests from your devices will go: Device → OpenWRT (port 53) → forwarded to AdGuard (port 5353) → upstream servers. AdGuard will block ads/tracker domains and otherwise forward to the chosen upstream servers.

8. Tweaks for Performance in AGH

AdGuard Home is quite efficient out of the box, but on a router (which also does NAT/routing) you may want to optimize some settings to keep DNS very fast and lightweight. Here are some tips:

  • Caching is your friend: AdGuard Home caches DNS replies so repeated queries return instantly from memory. By default the cache (for example, 10,000 entries) is usually fine. If you have plenty of RAM (the Pi 4 with 2+ GB does), consider increasing the cache size in AdGuard’s settings or enabling optimistic caching. Optimistic caching lets AdGuard serve a recently expired entry while refreshing it in the background, which can reduce latency. Meanwhile, you can reduce dnsmasq’s caching since AdGuard now handles DNS: set option cachesize '0' in /etc/config/dhcp to disable dnsmasq’s own cache. This avoids double-caching and ensures all queries go through AdGuard (so it can cache and log them centrally).
  • Use fast upstream DNS servers: I suggest using encrypted DNS servers for privacy; try DoT or DoQ with servers that are geographically close. You can measure latency via tools or AdGuard’s diagnostics. For example, I used Cloudflare and Quad9 DNS over DoH and saw round-trip times <20 ms. Avoid distant or slow servers (some default AdGuard servers can be >150 ms away). Pick two solid ones (Cloudflare, Quad9, etc.), and AdGuard will use them in round-robin or fallback mode. You can also enable “Parallel requests” in AdGuard’s DNS settings to query multiple upstream servers simultaneously—this often improves response times on cache misses but reducing privacy.
  • Right-size your block lists: AdGuard Home can handle big blocklists, but more rules means more memory and slightly longer lookups. If you subscribe to many public lists, there will be overlap. We’ll address deduping lists in the next section, which will this help a lot. If you want to skip that step, use only the lists you need. A well-curated few lists usually suffice. Lots of overlapping lists give diminishing returns and can slow things down a bit. You can monitor AdGuard’s memory usage on its dashboard. A Pi 4 (2 GB RAM) usually has plenty of headroom, but on older Pis (Pi 3 or Zero), you might use fewer lists to save RAM.
  • Allocate sufficient resources: Keep an eye on the router’s memory/CPU usage. The Pi 4 handles AdGuard + routing easily up to high loads. A Pi 5 would be even smoother. If you tried this on a Pi 3 or Zero, you might need to be more conservative: fewer blocklists, smaller cache, etc. But for typical home usage (even with 100+ devices), a Pi 4 should do just fine at Gigabit speeds.
  • Private reverse-DNS server: To resolve LAN hostnames in AdGuard’s query log, add 127.0.0.1 under “Private reverse DNS servers” in Settings → DNS Settings.
  • Test DNS throughput: You can stress-test AdGuard with tools like dig or dnsperf to see how many queries per second it handles. A Pi 4 can often do hundreds of QPS with AdGuard. If you notice any DNS lag when browsing (more than a few milliseconds), check AdGuard’s query logs and upstream stats to diagnose slow queries. Usually caching will serve >90% of requests instantly.

After these tweaks, your DNS resolution should feel very fast. In my setup, most queries resolve in <10 ms when cached. The browsing experience becomes seamless – ads and trackers simply fail to resolve, speeding up page loads and cleaning up content.

Next, we’ll look at an advanced tip for handling block lists more efficiently.

9. Merging Ad Block Lists

AdGuard Home lets you subscribe to multiple adblock filter lists (EasyList, AdGuard DNS filter, etc.). However, using many lists can create duplicate entries and lengthen update times. A neat trick is to merge all desired lists into one combined list, removing duplicates. This consolidated list can be maintained and hosted by yourself (for example via a GitHub Actions script), and then AdGuard only needs to fetch one URL.

I wrote a tool called AD-List-Merger to do this automatically. Here’s how:

  • Fork the merger repo: Go to the GitHub page for AD-List-Merger and fork it to your own account.

  • Add list URLs: In your fork, open the file (often named lists.txt). Paste the URLs of the filter lists you want to use. For example, you might include:

    • https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt (AdGuard DNS filters)
    • https://easylist.to/easylist/easylist.txt (EasyList)
    • https://easylist.to/easylist/easyprivacy.txt (EasyPrivacy)
    • https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts (hosts file with trackers blocked)
      (These are just examples; use whichever lists you trust and need for your setup.) Commit the changes.
  • Run the merger: The repository has a GitHub Actions workflow that runs on push or on a schedule (you can configure it). Trigger it by pushing a commit or manually if you set it up. The action will download each list, merge them line by line, remove duplicates, and produce one combined list as a new file.

  • Get the merged list URL: After the action finishes, find the raw URL of the merged list file (for example, use GitHub’s “Raw” link for the file in your repo).

  • Subscribe AdGuard Home to the merged list: In the AdGuard Home web UI, go to Filters > Ad Blocker > Add Filter > URL. Paste the URL of your merged list and give it a name (for instance, “My Merged Ads”). Click Add. AdGuard will fetch it and show how many rules it loaded.

  • Remove individual lists: If you had previously subscribed to separate lists that are now included in your merged list, you can disable or remove them to avoid duplication. The single merged list covers them all.

Now AdGuard Home uses one big, de-duplicated block list that you control. This can reduce memory usage (since each domain appears once) and slightly speed up list updates (only one URL to fetch). It also gives you flexibility to add or remove sources centrally. Note that if you only use a couple of lists, merging might not be worth it, but if you rely on many overlapping lists, this helps a lot.

10. Maximizing the Performance

The Raspberry Pi can be a very capable router, but we can enable a couple of OpenWRT features to squeeze out maximum throughput, especially for high-speed internet:

  • Software Flow Offloading: This is a firewall feature that speeds up NAT (routing) by shortcutting established connections. Normally, each packet goes through firewall rules, which consumes CPU. With flow offloading enabled, once a connection is recognized, the router bypasses most firewall processing for it. On devices without hardware NAT (like the Pi), this can dramatically improve throughput. Many users report near-gigabit routing on a Pi 4 when flow offloading is on.

    To enable it, go to LuCI > Network > Firewall > General Settings and check Enable Software flow offloading. (Don’t worry about hardware offloading – it doesn’t apply on the Pi.)

Caveat: Do not use flow offloading if you rely on SQM (Smart Queue Management) or other advanced traffic shaping on this device. Offloading bypasses those, so it’s one or the other. If your speeds are moderate (e.g. <100 Mbps) and you care more about bufferbloat management, skip offloading. But for raw throughput, it’s beneficial.

  • Enable Packet Steering: To improve multi-core performance, navigate in LuCI to Network > Interfaces, then click the Global Network Options tab. Check Enable Packet Steering. This ensures that all packets in the same traffic flow are handled by the same CPU core—reducing cache misses and boosting throughput.

  • Install and enable irqbalance: By default, network interrupts tend to run on CPU core 0, which can become a bottleneck under heavy load. The irqbalance daemon spreads IRQ (interrupt) handling across all CPU cores, so the work is distributed. On a multicore Pi (4 cores on Pi 4/5), this can significantly increase throughput and lower latency under load.

    Install it via LuCI (System > Software, search for “irqbalance” and install) or via SSH:

    Enable and start it:

    /etc/init.d/irqbalance enable
    /etc/init.d/irqbalance start
    

    By default, irqbalance will run with its standard settings, which are usually fine. (You can edit /etc/config/irqbalance if you want to pin certain cores or adjust policies, but it’s not required.)

On a Pi 4 with gigabit traffic, enabling irqbalance can allow hitting the full line rate (~940 Mbps) where one core alone could not. On Pi 5, the new interrupt controller still benefits from irqbalance.

After enabling these optimizations, you should see multiple CPU cores handling traffic. In LuCI, check Status > Realtime Graphs while doing a big download or speed test. You should see traffic on the Traffic graph and CPU usage spread across cores. It’s satisfying to see a small board pushing ~1 Gbps with the Pi’s CPU usage flattened out rather than maxing one core.

(One more thing: If you plan to run a VPN (WireGuard/OpenVPN) on the router, note that flow offloading does not accelerate encrypted VPN traffic – that still goes through the CPU. However, irqbalance will still help spread the crypto workload. The Pi 4/5 have decent crypto performance with hardware AES, so they do okay up to moderate speeds.)*

11. Upgrading Security of the Device

OpenWRT is more secure by default than many stock routers, but we can lock it down further. Treat your DIY router as a small production device. Here are recommended hardening steps:

  • LuCI on LAN and HTTPS only: By default, LuCI’s web server (uHTTPd) listens on all IPs on port 80. First, install HTTPS support. In LuCI, go to System > Software, click Update lists, then search for and install luci-app-uhttpd then navigate to Services > uHTTPd in LuCI. You’ll see sections for HTTP Listen and HTTPS Listen.

    In HTTP Listen, enter your LAN IPv4 address and port, for example:

    192.168.2.1:80
    

    In HTTPS Listen, enter your LAN IPv4 address (and any IPv6 addresses) with port 443. for example:

    192.168.2.1:443
    

    Check Redirect all HTTP to HTTPS if you want all web traffic automatically upgraded to HTTPS.

Now the web UI will be at https://192.168.2.1 (Your Local Ip) (you’ll get a certificate warning for the self-signed cert, which you can ignore or replace with your own). Crucially, LuCI won’t listen on the WAN IP at all. This encrypts your admin session and ensures only LAN hosts can reach the interface.

  • Firewall: drop vs reject: By default, OpenWRT’s WAN zone drops most incoming traffic but returns a “reject” (ICMP port unreachable). This reveals that a device exists. For a stealthier approach, change the WAN zone input and forward policies to DROP (no response). In LuCI, go to Network > Firewall > Zones, edit the WAN zone, set Input = Drop, Forward = Drop, then Save & Apply.

Now scans against your public IP will see closed ports (no reply), which is slightly better privacy.

  • **Enable BCP38 (Anti-Spoofing):**BCP38 stops your router from forwarding packets whose source IP addresses don’t belong to your LAN—an important measure to prevent reflection or amplification attacks. In LuCI, go to System > Software, click Update lists, then search for bcp38. Install both bcp38 and luci-app-bcp38. Navigate to Network > Firewall > Anti-Spoofing (BCP38). Select your WAN interface, check Enable, and click Save & Apply.

Now any LAN-side host trying to send spoofed traffic will be dropped by the router.

  • Disable unused services: If you don’t need a service, turn it off. For example, if you enabled UPnP (miniupnpd), be aware it can open ports dynamically. If you’re not using it, disable it in Services > UPnP (uncheck Enabled) and disable it on startup in System > Startup. Likewise, disable any other services you installed but don’t need. The Pi’s onboard Wi-Fi (if present) can also be disabled: go to Network > Wireless and turn off the radio. (Ethernet-only routers usually have Wi-Fi off by default, but double-check.)
  • Remote access considerations: It’s best not to expose LuCI or SSH to the internet. If you need remote administration, consider setting up a VPN (e.g. WireGuard) into your network, or use SSH on a non-standard port with key-only auth. Some people set up port knocking or even use a separate tiny Pi inside the LAN for management. In short, if you have a public IP and open any admin ports, lock them down (keys only, fail2ban, etc.).
  • Regular updates: Keep OpenWRT and its packages updated. Security fixes do come out. If you’re on a stable release, you can use tools like Attended Sysupgrade (ASU) or the command line to upgrade without losing your config. Always back up your settings before a big upgrade. Note: sometimes after upgrading OpenWRT, the AdGuard Home package might need re-installation or re-enablement if the binary changed. Also keep AdGuard Home itself updated

If you want, run a scan like GRC’s ShieldsUP against your WAN IP to verify ports are stealth (all should be “stealthed” or “closed”). With the default firewall plus these tweaks, you should see all green (no open ports).

Finally, remember that OpenWRT is a full Linux system. Apply good practices: only install packages from the official repos, be cautious with custom scripts, use strong passwords/keys, and monitor logs occasionally for odd activity.

Further Tweaks

A couple of extra tips you might consider as nice-to-haves:

  • Automatic internet-watchdog (Watchcat): Sometimes the internet link can freeze. OpenWRT has a package called watchcat (or similar watchdog services) that pings an external site and reboots the router if it loses connectivity. This can help recover from a hung state automatically. Install it (opkg install watchcat), configure it to ping an external IP (like 8.8.8.8), and it will reboot the Pi if pings fail repeatedly.
  • Scheduled reboot: Schedule a periodic reboot (e.g. nightly or weekly) via cron to clear any potential memory leaks or stale states. You could add a cron job like 20 6 * * * sleep 70 && touch /etc/banner && reboot (every day at 6:20 AM) or adjust as desired. This is optional but can help long-term stability.

Final Words

You now have a Raspberry Pi acting as a full-featured home router with OpenWRT and AdGuard Home. We went from flashing a custom OpenWRT image to expanding the storage, setting up dual network interfaces, installing a network-wide ad blocker, and tuning both performance and security. Now you have complete control and insight into your network.

OpenWRT and AdGuard Home make a formidable combo for a modern home network: fast, private, and customizable. Your router is now filtering ads and trackers at the DNS level for all devices, with gigabit throughput and strong security.