Notes · Organized thoughts & experiments


Building a Custom NixOS Router for Freedom Internet

Due to the persistent lack of IPv6 support at Odido, I finally made the switch to Freedom Internet. My primary goal was to learn how IPv6 works, and frankly, I was done with Odido's lax attitude toward their customers.

From pfSense to NixOS

My initial setup was running pfSense 2.7. The transition to Freedom was actually seamless at first; after configuring DHCPv6, I had an IPv6 connection immediately. However, things went south when I decided to update to version 2.8 to stay current. Suddenly -bam- my router hung indefinitely while loading if_pppoe.

I had been wanting to try OPNsense for a while, so I took the opportunity to install it, but I simply couldn't get it online. I reinstalled pfSense 2.7, but strangely encountered the same issues. That’s when I had the "brilliant" idea to build my own router from scratch using NixOS. After 15 hours of debugging, scouring blog posts, and plenty of "vibecoding," I finally achieved a working dual-stack connection!

This blog was written after a couple of months using this setup and adjusted with changes beyond my initial setup.

Technical Notes

For those looking to attempt a similar NixOS setup, here are the key takeaways from my configuration:

IPv6 Prefix Delegation

Freedom provides a /48 IPv6 prefix via DHCPv6-PD. Configuring this with systemd-networkd is straightforward. On each LAN interface, I enable prefix delegation and router advertisements:

networkConfig = {
  IPv6SendRA = "yes";
  DHCPPrefixDelegation = "yes";
};

systemd-networkd then:

  1. Requests the prefix from the upstream WAN interface
  2. Automatically assigns a /64 subnet to the LAN interface
  3. Sends Router Advertisements so devices can auto-configure addresses
  4. Handles renewals transparently

This results in devices getting globally routable IPv6 addresses without any manual subnet management or NAT configuration.

nftables: Learning by Doing

I’ll be the first to admit I don’t know enough about nftables yet, but writing things neatly into my NixOS configuration give me extra motivation to have a deeper understanding of such things. For example setting time-based access control for my robot vacuum:

iifname "wifi" ip saddr ${dreame} oifname "peepee" meta hour "10:00"-"19:00" accept comment "Dreame Internet Access Window";
iifname "wifi" ip saddr ${dreame} oifname "peepee" drop comment "Block Dreame Internet outside schedule";

Now it can only phone home between 10 AM and 7 PM. I run ntopng on this same router and noticed it's phoning home to Chinese Alibaba cloud servers all day long. This rule gives it a strict time window for internet access while keeping it blocked the rest of the day.

Other fun rules include:

Hardware and Performance

The router runs on a Protectli V1410, a fanless mini PC with four 2.5GbE ports and an Intel N100 CPU. It's silent, low-power, and more than capable of handling gigabit fiber with all the services I run on it.

Performance-wise, the setup is pretty solid: I’m currently hitting 910 Mbps down and ~830 Mbps up. Good, but I've had pretty consistent 1Gbps up/down on my Odido line using their ONT and router.

Configuration Files

If you want to see the code behind this setup, you can find my NixOS modules here:

Note: These files will likely evolve. If you're reading this in the future, be sure to check the master branch for the latest updates.

Resources

I've leaned on multiple online blogs and resources, in no particular order: