On most laptops I use wg-quick to setup my WireGuard tunnels, but recently I got a bit curious about using systemd-networkd to the the same.

In particular I want the same split-tunnel setup that I’ve used before to work also with systemd-networkd.

Worth noticing is that the setup I describe here will automatically start the tunnel when launching the computer. I haven’t really investigated if it is possible to do it on demand. Edit 2024-04-19: It is possible, thanks for some great feedback from axel

You need to create two different files to get this to work: /etc/systemd/network/99-wg0.netdev and /etc/systemd/network/99-wg0.network.

/etc/systemd/network/99-wg0.netdev

[NetDev]
Name=wg0
Kind=wireguard
Description=Wireguard tunnel wg0

[WireGuard]
PrivateKey=<PRIVATE KEY>

[WireGuardPeer]
PublicKey=<PUBLIC KEY OF REMOTE ENDPOINT>
PresharedKey=<PRESHARED KEY>
AllowedIPs=10.0.0.0/20
Endpoint=<IP ADDRESS:PORT OF REMOTE ENDPOINT>

Note that the captalization of WireGuard is important! (Spent quite some time on this…)

Since I’m setting up a split tunnel, I set AllowedIPs to the IP-ranges I want to reach through the tunnel, in CIDR-notation. In this case 10.0.0.0/20 which translates to 10.0.0.0 – 10.0.15.255, which covers the relevant parts of my local network.

/etc/systemd/network/99-wg0.network

[Match]
Name=wg0

[Network]
Address=10.0.7.2/32
# ~zozs.se causes dns lookups for domains ending in .zozs.se to be routed
# to the DNS servers of this link, but doesn't affect other domains.
Domains=~zozs.se
DNS=10.0.7.254

[Route]
Destination=10.0.0.0/20
Scope=link

In this file, routes and DNS is setup. This is only relevant if you also use systemd-resolved for DNS. In this example, it will cause all DNS requests for zozs.se and subdomains to use a DNS server on the WireGuard tunnel, instead of the system’s regular DNS. This ensures my split-tunnel DNS works correctly, but doesn’t affect any other DNS requests.

Final notes

Remember that you need to reload the configuration. I do this by using systemctl restart systemd-networkd.service.

After this, you can check the status with networkctl. It should show an output similar to this:

❯ networkctl
IDX LINK     TYPE      OPERATIONAL SETUP
  1 lo       loopback  carrier     unmanaged
  2 enp1s0f0 ether     no-carrier  configuring
  3 wg0      wireguard routable    configured
  5 wlan0    wlan      routable    configured

4 links listed.