Published on

Arch Linux Home Router Gateway using systemd-networkd


This is part of a series of posts about building your own Linux home router using systemd-networkd. The posts are organized as follows:

  1. Connecting to CenturyLink using PPPoE and systemd-networkd
  2. Network Architecture and VLAN configuration
  3. Firewalld policy-based access control between zones

This guide assumes you have a working knowledge of Linux, networking and routing concepts. This guide is built on Arch, but should be roughly translatable to other Linux distributions which have Systemd Networkd, Firewalld and pppd packaged at a relatively recent version.

In this post, we'll cover the new home architecture as well as device and VLAN configuration using systemd-networkd.


I love making architecture diagrams as they drastically aid in developing the mental model needed to build the system.

My requirements for this network are fairly simple:

  1. I want policy-based access control between subnets/VLANs.
  2. I want to centralize DHCP and DNS management.
  3. There is no 3.

One major drawback of this particular architecture is that we've got a major bottleneck at the router for inter-vlan traffic. If we're ever trying to stream 4k in 60fps on multiple devices, that may become an issue.

Configuring the Router

As with any linux router, you must enable IP forwarding first. This enables it at boot:

cat >/etc/sysctl.d/99-ip_forward.conf<<EOF
net.ipv4.ip_forward = 1

To apply this now, you can run: sysctl -w net.ipv4.ip_forward = 1.

Next comes systemd-networkd setup which is beautifully simple. Systemd-networkd uses the same familiar unit-file syntax that all other systemd services and triggers use and is very readable.

First we should configure the physical network device facing our internal network (enp3s0). In the previous post we configured the internet-facing network adapter (enp2s0). We configure the device as a VLAN trunk using the following config:

cat >/etc/systemd/network/<<EOF


That indicates to systemd-networkd to look for those children devices and configure this device as the parent (VLAN trunk).

Next, we need to configure those virtual network adapters using *.netdev files. I'm only going to show one example here, since they're all pretty much identical in my setup. For VLAN 10, that looks like:

cat >enp3s0.10.netdev<<EOF


After running networkctl reload, this file will create the device without any further network configuration. Of course, we do want network configuration and more. For my purposes, I'll need a DHCP server and DNS configured on each subnet. Remember what I mentioned earlier about centralized DHCP? This is what I meant.

cat ><<EOF



This file defines a number of things:

  1. The device's statically allocated address
  2. The subnet (derived from the address)
  3. The DHCP server settings
  4. The DNS settings passed to the clients
  5. And IPMasquerade which enables NAT through the router.

This configuration is identical to the other VLANs but with the changed VLAN Tag on the match block to indicate the correct device this network file is configuring.

I like to start DHCP addresses at x.100 because it makes them easy to identify visually. I'll never have 100 static IPs in use and I also doubt I'll have 154ish dhcp devices connected either so I set the pool size and offset to 100 for convenience.

The other devices and subnets are left as an exercise for the reader! Hint: you can pretty much just sed the vlan tag with the new one for each device. In the next post we'll cover the most important component of the home network: the firewall.

Coming from pf I have pretty high standards, but I'm happy to report that configuring firewalld wasn't as troublesome as I initially imagined. Stay tuned! It's gonna be 🔥🔥🔥🧱.