Roonbridge on Linux doesn't seem to recognize Wireguard network interface

I’ve been experimenting with RoonBridge over a Wireguard VPN connection so that my local audio devices are recognized by a RoonServer on a remote network.

I’m running Wireguard with the exact same configuration on three different machines:
– My MacBook Air under MacOS Monterey 12.6.3
– An old MacBook Pro under Ubuntu Linux 22.04.1
– A Raspberry Pi under DietPi v8.13.2.

The configuration for Wireguard is exactly the same on all three machines (different IP addresses of course).

All three machines are running RoonBridge 1125.

However, this setup only works under MacOS. From RoonBridge_log.txt on that machine:

02/01 17:58:12 Trace: [ipaddresses] FOUND lo0 127.0.0.1
02/01 17:58:12 Trace: [ipaddresses] FOUND en0 192.168.178.76
02/01 17:58:12 Trace: [ipaddresses] FOUND utun7 192.168.0.202

Neither of the Linux machines seems to recognize the Wireguard network interface (called wg0 there, not utun7 as on the Mac):

02/01 18:01:03 Trace: [ipaddresses] SKIPPED lo: not up
02/01 18:01:03 Trace: [ipaddresses] FOUND eth0 192.168.178.110
02/01 18:01:03 Trace: [ipaddresses] SKIPPED wg0: not up

This is from the DietPi, but the Ubuntu machine behaves the same. Interestingly, on both machines RoonBridge also claims that the local interface isn’t up.

On the remote RoonServer, this results in the following lines in the server logs:

02/01 19:14:42 Trace: [SOOD] Adding User IP 192.168.0.202
02/01 19:16:27 Trace: [SOOD] Adding User IP 192.168.178.76
02/01 19:16:27 Trace: [SOOD] Adding User IP 192.168.178.110
02/01 19:16:27 Trace: [SOOD] Adding User IP 192.168.178.72
02/01 19:16:27 Trace: [SOOD] Adding User IP 192.168.0.115

The first line is my MacBook Air’s Wireguard interface, the second its local ethernet interface. Then there are the DietPi’s and the Ubuntu machine’s local ethernet interfaces. Their Wireguard tunnel interfaces don’t show up. The last line is the server itself.

Of course the server doesn’t know how to connect to any of the remote IPs (on the 192.168.178.0 subnet), only those that reside in its own subnet. Which means it can work with the MacBook Air’s Wireguard interface, and it finds any endpoints connected to that, but not with either of the Linux machines.

Which brings me to my question: What needs to be done to have Roonbridge acknowledge the wg0 interfaces that it currently skips on the Linux machines?

1 Like

For anyone landing here from a search: this bug is still present in current Roon Bridge builds (I’m on 2.60). The root cause is that Bridge’s interface enumeration checks the kernel’s operstate field, and WireGuard interfaces report operstate=unknown rather than up. Bridge skips them with the SKIPPED wg0: not up message in the logs.

You can confirm it on your own box:

$ cat /sys/class/net/wg0/operstate
unknown
$ cat /sys/class/net/eth0/operstate
up

ip link set wg0 up doesn’t fix it — operstate for tunnel interfaces is derived from carrier state, not user-settable.

The workaround that worked for me: create a veth pair, assign one end the same IP as your WireGuard tunnel, and let normal routing carry the traffic.

ip link add roonvpn type veth peer name roonvpn-peer
ip link set roonvpn up
ip link set roonvpn-peer up
ip addr add 10.0.0.2/32 dev roonvpn   # use your own wg tunnel IP here

Roon Bridge will happily bind to roonvpn because veth interfaces report operstate=up. The actual packets still flow through wg0 because the IP routing table sends Core’s subnet via the WireGuard interface — the veth is purely there to satisfy Bridge’s interface check.

I wrapped this in a small systemd unit (roon-veth.service) and added it as an After= dependency on roonbridge.service via a drop-in at /etc/systemd/system/roonbridge.service.d/after-veth.conf so the veth always exists before Bridge starts. Stable across reboots since I deployed it about a week ago.

Bonus, if you’re trying to use Bridge across a Layer 3 VPN like WireGuard: the SOOD discovery packets (UDP/9003 to 239.255.90.90 and the subnet broadcast address) won’t traverse the tunnel by default, so even after you fix the interface binding, Bridge still won’t find Core. People usually solve this with udp-proxy-2020, but you can also just NAT the broadcast packets to Core’s unicast IP with three iptables rules:

iptables -t nat -A OUTPUT -p udp --dport 9003 -d 255.255.255.255 \
  -j DNAT --to-destination 10.0.1.10:9003
iptables -t nat -A OUTPUT -p udp --dport 9003 -d 10.0.2.255 \
  -j DNAT --to-destination 10.0.1.10:9003
iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE

Replace 10.0.1.10 with your Roon Core’s IP, 10.0.2.255 with your local subnet’s broadcast address, and wg0 with your WireGuard interface name. Once Bridge can (a) bind to a “real” interface and (b) reach Core’s discovery port via unicast, the rest of RAAT just works over the tunnel.

Hope this saves someone the hours I spent figuring it out.

Credit where it’s due: I worked through the diagnosis and the veth approach with claude code. The operstate root cause and the veth-to-satisfy-the-interface-check trick came out of that back-and-forth.