Posted on Oct 11, 2025
I’ve just replaced our old home router with a tiny OpenBSD router, and I’ve been surprised and delighted by it.
The old router - a TEW813-DRU - was getting long in the tooth. Made in 2014, it was relieved of WiFi gateway duties when the pandemic demanded less glitchy videoconferencing than it could support1, and was only still around because it can route full gigabit to the WAN port2 - and perhaps more importantly, allows DHCP to be switched off so that our Pi-Hole can take over that task, along with DNS resolution. For a long time, it also routed IPv6 very happily - but that seemed to break somehow a year or so ago (I susp…
Posted on Oct 11, 2025
I’ve just replaced our old home router with a tiny OpenBSD router, and I’ve been surprised and delighted by it.
The old router - a TEW813-DRU - was getting long in the tooth. Made in 2014, it was relieved of WiFi gateway duties when the pandemic demanded less glitchy videoconferencing than it could support1, and was only still around because it can route full gigabit to the WAN port2 - and perhaps more importantly, allows DHCP to be switched off so that our Pi-Hole can take over that task, along with DNS resolution. For a long time, it also routed IPv6 very happily - but that seemed to break somehow a year or so ago (I suspect config changes at our ISP), so we’ve been IPv4 luddites since then.
Why BSD? I guess I’d have to admit that I have an affinity for oddball setups - but I also wondered if it’d be simpler than a Linux solution. I love Linux - in fact, this router is running in a VM on a Linux system that’s also running Home Assistant (more about that in the future, perhaps), but over time distros have become super complicated. BSD has a reputation for being ‘simpler’.
Often, my oddball setups mean a bunch of wasted time and a return to the mainstream, but I’m happy to say that I do not regret this choice at all. OpenBSD is wonderful for this task. It’s small. I allocated 512MB of RAM to the VM, and it’s using 183MB of it. It’s using about 2G of disk space, without really any attempt on my part to keep it small beyond the obvious (no windowing system or games installed). Installation was super simple3.
After installation: run top and there’s basically nothing running beyond what’s needed to make networking work. It’s just so refreshingly empty. No maze of SystemD services here4.
Most everything is configured using text files in /etc - usually with simple examples provided. System service configuration is just done in a text file too - though there’s also a rcctl command line if you’d rather.
An unforeseen bonus: web searches for help and advice on setting things up, well, they work! There’s no morass of SEO spam and AI slop aimed at OpenBSD users. These days, with Linux, I find myself wading through a plethora of web sites with names like tech-4u.com and howto-homeonlinux-good.info containing at best reworded man pages and, more often, plausible but wrong solutions with recipe-site level introductory paragraphs. Searching for OpenBSD advice, and you’ll still find thoughtfully written personal and academic blog posts, mailing list archives with questions and answers from real people, and personal sites with notes-to-self. It’s like, in a good way, the 2000s again.
Did I say “recipe-site level introductory paragraphs”? Let’s get to it - how to set up an OpenBSD-based router for home use. NAT for IPv4, native IPv6, and all that good stuff.
Ingredients
You’ll need a computer with two network ports (not necessarily x86 - there are a plethora of platforms supported by OpenBSD), well, that’s about it.
My hardware is a Mac mini (Late 2014), with an Intel Core i5 4260u, with an SSD5 replacing the original hard drive. A cheap used Apple Thunderbolt Ethernet adapter from eBay provides the second port6.
It’s not really important for this article other than how it affects the network port naming, but I’m actually running Debian on this hardware, with the OpenBSD router running inside a QEMU virtual machine. The WAN is connected to the built-in gigabit port, with the PCI hardware passed through directly to the VM for OpenBSD to manage natively. The ‘LAN’ port in OpenBSD is a QEMU virtual network interface7.
Okay, first, install! The OpenBSD handbook has comprehensive installation instructions. TL;DR: download the appropriate disk image8, and install it on your hardware. I minused-out all the X11-related things, and the games package. If you were going for a super-small installation, you could also disable the compilers package - I installed that but have never needed them.

After installation was complete, I pkg-added a couple of my can’t-live-without packages - vim, fish, htop - and went about setting up the router.
IPv4 with NAT, like in the olden days.
First, IPv4 (with network address translation). Network setup on BSD is super easy! Just edit the hostname.<interface> files in /etc. You can see the names for the network interfaces by running ifconfig - and ignoring lo0 (the “software loopback” interface), enc0 (the “virtual interface for ipsec traffic”), and pflog0 (the “packet filter logging interface”).
For me, I could see bge0, and vio0. Generally, the names are abbreviations of something to do with the manufacturer of the interface (or, really, the driver). I know that the Mac’s built-in network interface uses a Broadcom chip, so that’s the bge0, and vio0 is the virtual QEMU network card.
As root, I edited /etc/hostname.bge0 - the one I’ll use for the WAN port:
inet autoconf
And for the LAN port, /etc/hostname.vio0:
inet 192.168.25.1 255.255.255.0
Then restarted the network interfaces: sh /etc/netstart bge0, sh /etc/netstart vio0
That’s enough to get IPv4 networking set up on OpenBSD!
jamie@vrouter ~> ping example.com
ping: Warning: example.com has multiple addresses; using 23.215.0.138
PING example.com (23.215.0.138): 56 data bytes
64 bytes from 23.215.0.138: icmp_seq=0 ttl=51 time=23.458 ms
64 bytes from 23.215.0.138: icmp_seq=1 ttl=51 time=22.678 ms
64 bytes from 23.215.0.138: icmp_seq=2 ttl=51 time=23.155 ms
^C
--- example.com ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 22.678/23.147/23.458/0.291 ms
We still need routing and NATting between LAN and WAN - that’s also relatively simple. OpenBSD’s built-in pf (‘Packet Filter’) system can do that. Just edit /etc/pf.conf.
Here’s what I have - if you’re curious about what this does in more detail, man pf.conf is well written and comprehensive.
# Interfaces
wan_if = "bge0"
lan_if = "vio0"
# Options
set skip on lo
set block-policy return
# 'Normalization' (not sure this is really necessary nowadays)
match in all scrub (random-id)
# Activate spoofing protection for all interfaces
antispoof quick for $lan_if
block in quick from urpf-failed
# NAT for LAN to WAN (IPv4)
match out on $wan_if inet from $lan_if:network to any nat-to ($wan_if)
# Default deny
block all
# Allow all on LAN
pass on $lan_if
# Allow outbound on WAN (stateful - return traffic automatically allowed)
pass out on $wan_if
# WAN inbound - only essential ICMP ('ping') for MTU discovery
pass in on $wan_if inet proto icmp icmp-type { unreach echoreq }
A pfctl -nf /etc/pf.conf will check the file for errors, and pfctl -f /etc/pf.conf will load them into the running packet filter without the need to reboot.
All that’s remaining is to switch on IP forwarding - sysctl net.inet.ip.forwarding=1 will change the current setting, and adding the line net.inet.ip.forwarding=1 to /etc/sysctl.conf (which might not exist yet - just create it) will change the default behavior after a reboot.
[Your LAN will also need a way for its inhabitants to get IP addresses, and a way to resolve domain names. Our network has a Pi-Hole, which provides both DHCP and DNS services to handle these tasks. If you want it to, this OpenBSD router can easily handle them too - but this post isn’t going to tell you how, because I haven’t done it.]
From my Mac, on the LAN:
jamie@Jamies-MacBook-Air ~> ping example.com
PING example.com (23.220.75.245): 56 data bytes
64 bytes from 23.220.75.245: icmp_seq=0 ttl=50 time=75.331 ms
64 bytes from 23.220.75.245: icmp_seq=1 ttl=50 time=76.191 ms
64 bytes from 23.220.75.245: icmp_seq=2 ttl=50 time=97.510 ms
^C
--- example.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 75.331/83.011/97.510/10.259 ms
Break out the champagne! We have an IPv4-only home router!
IPv6. At last, an internet protocol for the ’90s!
Getting IPv6 working is a little bit more work, but not as much as you might think!
You’ll find a lot of blog and forum posts around the web saying that you need to install extra packages to make being an IPv6 gateway work - but it’s not true anymore!9
First, let’s set up the WAN port. /etc/hostname.bge0 needs another line:
inet autoconf
inet6 autoconf
Then restart the network interface again: sh /etc/netstart bge0.
That might actually be enough to get local IPv6 working ‘outwards’ (like, you can ping6 things from the router)10 - but it’s not enough to propagate it to the LAN.
We need something to request a block of IPv6 addresses for the LAN clients (from the WAN), something to actually ‘delegate’ addresses to the LAN, and something to route packets.
The built-in dhcp6leased can request the addresses. Configure it in /etc/dhcp6leased.conf:
request prefix delegation on bge0 for { vio0 }
Next, something to delegate the addresses: rad, the Router Advertisement Daemon. We just need to tell it to do it on the LAN port. In /etc/rad.conf:
interface vio0
Last, we need to configure the packet filter system to deal with IPv6 too. Append to /etc/pf.conf:
# IPv6
icmp6_types = "{ echoreq unreach toobig routersol routeradv neighbrsol neighbradv }"
pass in on $wan_if inet6 proto icmp6 icmp6-type $icmp6_types
pass out on $wan_if inet6 proto icmp6 icmp6-type $icmp6_types
pass in on $wan_if inet6 proto udp from fe80::/10 port dhcpv6-server to fe80::/10 port dhcpv6-client no state
pass out on $wan_if inet6 proto udp from fe80::/10 port dhcpv6-client to fe80::/10 port dhcpv6-server no state
pass out on $wan_if inet6
Here, we’re letting ICMP (ping) packets of the types necessary for IPv6 autoconfiguration (so-called ‘Stateless Address Auto-configuration’ - or ‘SLAAC’) through, we’re letting DHCPv6 packets from the ISP into the WAN port to allow it to delegate addresses to us, and otherwise just passing out any IPv6 connections LAN clients might make to the outside world.
Once again, pfctl -nf /etc/pf.conf will check the file for errors, and pfctl -f /etc/pf.conf will load it dynamically without needing a reboot.
And again, just like for IPv4, we need to switch on IPv6 forwarding - sysctl net.inet6.ip6.forwarding=1, and add the line net.inet6.ip6.forwarding=1 to /etc/sysctl.conf.
We’ll now need to switch a few services on - the IPv4 ones were on by default, but the IPv6 ones we just configured are not. There’s a command to do this, rcctl (although, following the OpenBSD philosophy, it’s actually just changing a text file in /etc - /etc/rc.conf.local).
rcctl enable dhcp6leased
rcctl start dhcp6leased
rcctl enable rad
rcctl start rad
And we’re done!
jamie@vrouter ~> ping6 example.com
ping6: Warning: example.com has multiple addresses; using 2600:1406:bc00:53::b81e:94ce
PING example.com (2600:1406:bc00:53::b81e:94ce): 56 data bytes
64 bytes from 2600:1406:bc00:53::b81e:94ce: icmp_seq=0 hlim=51 time=71.381 ms
64 bytes from 2600:1406:bc00:53::b81e:94ce: icmp_seq=1 hlim=51 time=79.220 ms
64 bytes from 2600:1406:bc00:53::b81e:94ce: icmp_seq=2 hlim=51 time=71.501 ms
^C
--- example.com ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 71.381/73.436/79.220/3.341 ms
and connected clients should be able to too - here, from my Mac:
jamie@Jamies-MacBook-Air ~> ping6 example.com
PING6(56=40+8+8 bytes) <--REDACTED--> --> 2600:1408:ec00:36::1736:7f24
16 bytes from 2600:1408:ec00:36::1736:7f24, icmp_seq=0 hlim=50 time=33.088 ms
16 bytes from 2600:1408:ec00:36::1736:7f24, icmp_seq=1 hlim=50 time=30.607 ms
16 bytes from 2600:1408:ec00:36::1736:7f24, icmp_seq=2 hlim=50 time=26.671 ms
^C
--- example.com ping6 statistics ---
4 packets transmitted, 3 packets received, 25.0% packet loss
round-trip min/avg/max/std-dev = 26.671/30.122/33.088/2.642 ms
Its impressive sounding “two concurrent wireless networks—a high performance 867 Mbps Wireless AC network and a 300 Mbps Wireless N network” just weren’t up to the task. A WiFi-6 router placed more centrally in the house fixed things. ↩︎ 1.
Yes, full gigabit networking has been around for far longer than eleven years at this point, but a surprising number of consumer routers at that time, despite having gigabit ports on both the LAN and WAN sides, didn’t have the processing power to actually route at full gigabit speeds between them. ↩︎ 1.
The only ‘gotcha’ I experienced during installation was that, because I accidentally initially ran my VM with access to only one processor core, the installed kernel didn’t support multiple CPUs - so when I switched to give it access to the others it couldn’t use them. But it was easy to re-install when I noticed. ↩︎ 1.
Don’t get me wrong, I kind of love SystemD too. It is complex, but it makes managing the services, containers and, now, router VM on the Debian system that all this is on top of super easy. Multitudes, eh? ↩︎ 1.
A pretty bad one, TBH. It’s reportedly made by “Gigastone”, and I bought it cheap years ago at an already-obviously-failing Fry’s, RIP. ↩︎ 1.
At first, I used an old Aukey USB 3.0 Hub with a built-in gigabit port. I first assigned it as the WAN port in OpenBSD, but it used tons of CPU and kept failing under stress (ure0: usb error on tx: IN_PROGRESS - seems to be a known issue with the OpenBSD driver for the slightly esoteric chip it uses). I switched around to use it as the LAN port (and give the built-in PCI Ethernet to OpenBSD) and that made things work well - stable and full throughput - but it somehow used three watts more than the Mac did by itself! Eight bucks later and the Thunderbolt port is working great. Although it still seems to use 1W, which seems like a lot to me for ‘just’ a network port. Perhaps I should buy a more modern one? ↩︎ 1.
On the Debian host, the virtual network port is backed by a TAP device, set up to bridge with the Thunderbolt Ethernet adapter - effectively connecting the virtio and Thunderbolt ports as if both were real and plugged into the same Ethernet switch. ↩︎ 1.
Note that 64-bit Intel processors are known in the OpenBSD world as AMD64. AMD actually invented the 64-bit x86 instruction set while Intel was trying to convince the world that Itanium was a great idea. ↩︎
1.
Since version 7.6, OpenBSD has included dhcp6leased - no need to install and configure the dhcpcd package any longer. ↩︎
1.
It’s not enough for me - my ISP (Spectrum) seems to require DHCPv6 to get an IPv6 address - but yours might support SLAAC autoconfiguration which I think would be enough to make local IPv6 work. ↩︎