I’m a bit of a home networking nerd. After many iterations, I’ve settled on a custom built Linux home router. My goals are:
- Secure as possible.
- Supports fq_codel and the ability to disable kernel offloads to favor latency and eliminate bufferbloat.
You can do a basic test of your router setup using http://www.dslreports.com/speedtest. The important thing is the bufferbloat rating. You want this as good as possible (A+ ideally) to avoid lag during normal or heavy usage. Here’s my result on UK Fibre-To-The-Cabinet (FTTC):
First you need to choose your hardware. There are many options here. I wanted something compact and fanless, similar to a consumer embedded router but with an x86 processor. I chose a Netgate RCC-VE 2440 which cost $349. It’s a bit on the pricey side, but suits me perfectly. It’s discontinued now, but there is the similar, cheaper Netgate MBT-2220 for $250 which also looks very good.
I have a separate Ubiquiti AC Lite for my wifi needs, which sits on the LAN.
You need to install a Linux OS. I chose Debian stretch since it’s relatively stable and I like Debian based OSs. It also supports fq_codel out of the box.
I won’t cover installing or using Linux here, there are plenty of guides for that. Instead, I’ll give a summary of the software I installed on the router and configuration settings I used for each.
The basics are:
/etc/network/interfaces– The main configuration for Debian’s networking.
- shorewall – Firewall/iptables management. Can setup almost anything you need a router to do (port forwarding, masquerading, etc.).
- isc-dhcp-server – DHCP server for managing IP addresses on the LAN.
- unbound – DNS server for resolving and caching DNS queries. Also handles blocklists for blocking ads and anything else.
You also probably want to manage all this in an automated fashion. I use ansible checked into a git repo. It’s up to you what to use – you could also do everything manually if you want, but it will be painful to maintain it.
This is the main configuration for the network interfaces on your router. Since my router has 4 routeable ports, I software bridge them all to give me some additional ports. You could also create separate networks if you’d like. This is also where you configure PPPoE if needed. Here’s a sample configuration based on my own setup:
I’ve disabled GRO/GSO/TSO to prevent the kernel from messing around with packets by coalescing them. This may increase CPU usage and reduce throughput (theoretically), but the benefits are improved latencies (also somewhat theoretical).
The ppp configuration is placed in
/etc/ppp/peers/provider. There should already be a sample file there. The only changes I made were to the
mru values (setting each of those to 1500, as ppp will default to 1492 otherwise).
To disable the offloads for ppp, you need to create a file /etc/ppp/ip-up.d/ethtool:
This will get run every time the ppp interface comes up.
Not much else to add here, that’s about it. See the manpages for more details.
Shorewall handles the firewall, masquerade, and fq_codel configuration. It can also do port forwarding for you. Shorewall is managed by creating a bunch of small files in the /etc/shorewall directory. Each file has its own manpage with details, e.g.
man shorewall-interfaces. I suggest following the excellent manpages for the most part.
Here is my sample configuration for fq_codel, which is easy to setup in shorewall.
Those three files are all that’s needed to get fq_codel working. Afterwards you should see a significant improvement in your bufferbloat scores, assuming you set upload and download speeds correctly.
Finally, here is the rest of the config:
If using ppp, make sure you add “wait_interface=ppp0” to
This is the open source dhcp server the router will use to assign IPs on your LAN. It supports static IPs and all sorts of dhcp options. It also has a built in cli for viewing leases. If you copy oui.txt (you can google and find this file easily) to
/usr/local/etc/oui.txt, the cli will show you the maker of the device as well – e.g. Apple, Nintendo, and so on.
Here is a sample configuration assigning a subnet to the LAN bridge as well as some static IPs. I’ve also set it to provide the router’s IP as the preferred DNS server.
/etc/default/isc-dhcp-server with the listening interfaces. Mine is
I use unbound as the DNS server after using it a lot in pfSense. I prefer unbound because the DNS requests are resolved directly with the root servers and authoritative servers for the relevant domains. By enabling preemptive lookups and caching, we can also avoid some of the performance impact of doing so.
Using unbound isn’t strictly necessary – dnsmasq is also a nice alternative that will be faster generally (but you’ll need to use an external DNS resolver).
Fortunately the debian package makes it quite easy to use unbound – it handles auto updates of the root key and so on. This is setup in
I have a file at
/etc/unbound/unbound.conf.d/home.conf with the main configuration.
As can be seen I also create a few static domain entries so I can reference them without needing to remember the IP.
Finally, I create a
block.conf using https://github.com/jodrell/unbound-block-hosts. This is placed at
The important part for security, apart from the firewall, is enabling automatic updates so we always have the latest security patches installed. By doing this your router is going to be more secure than almost any home router out there. This is a little bit clunky to setup in debian, but works well once done.
First, install the
unattended-upgrades package. Then create /etc/apt/apt.conf.d/20auto-upgrades:
Then update /etc/apt/apt.conf.d/50unattended-upgrades:
That should be it to ensure auto updates happen.
Some final touches
- Enable persistent journal logs by setting
ntpto keep system time up to date.
And that’s it! From here the sky is the limit. You can add any custom functionality you desire, like strongswan for external VPN. I’ve been using this setup for a couple years now with great success.