WireGuard’s simplicity and performance have made it the VPN of choice for many administrators, but when you need to route traffic between multiple subnets across several sites or through a hub-and-spoke topology, details matter. This article dives into the practical and technical aspects of routing traffic between multiple subnets with WireGuard, covering configuration patterns, kernel/networking tweaks, policy routing, NAT considerations, and troubleshooting tips for production environments.

Understanding the Core Concepts

Before jumping into configuration, it’s essential to understand a few core concepts that drive how WireGuard handles traffic:

  • Peers and AllowedIPs: Each WireGuard peer advertises one or more IP prefixes via the AllowedIPs field. These prefixes determine which traffic is routed into the WireGuard tunnel.
  • Endpoint and PersistentKeepalive: Endpoint defines where to send packets for a peer; PersistentKeepalive helps keep NAT mappings alive for peers behind NATs.
  • Linux kernel routing: WireGuard inserts routes based on AllowedIPs when the interface is active, but complex topologies often require explicit routes, policy routing, and iptables/nftables rules.
  • IP Forwarding: The kernel must be configured to forward packets between interfaces (sysctl net.ipv4.ip_forward=1).

Topology Patterns

Common topologies for multi-subnet routing with WireGuard include:

  • Full mesh: Every site peers directly with every other site. Simple AllowedIPs but scales poorly as sites grow.
  • Hub-and-spoke: A central hub routes traffic between spokes. Scales well and centralizes control, but the hub becomes a single point of failure/bottleneck.
  • Partial mesh with route propagation: A hybrid where some direct links exist and others use the hub; requires explicit route propagation either manually or via a routing protocol.

Example Use Case

Suppose you have three sites:

  • Site A: local subnet 10.10.1.0/24
  • Site B: local subnet 10.10.2.0/24
  • Hub: central server with subnet 10.10.0.0/24 and public IP accessible

Goal: allow hosts in 10.10.1.0/24 and 10.10.2.0/24 to reach each other via the Hub using WireGuard.

WireGuard Interface Addresses

Assign a unique /24 or /32 for each site on the WireGuard network, for example:

  • Hub wg0: 10.0.0.1/24
  • Site A wg0: 10.0.0.2/24
  • Site B wg0: 10.0.0.3/24

Keep these addresses separate from the local LAN subnets (10.10.x.0/24) to avoid ambiguity.

Hub Configuration

On the hub server (assume Debian/Ubuntu), create /etc/wireguard/wg0.conf with the following pattern:

Important kernel settings: ensure forwarding and proper sysctl entries before enabling WireGuard.

Example sysctl settings (persist in /etc/sysctl.d/99-wireguard.conf):

  • net.ipv4.ip_forward=1
  • net.ipv6.conf.all.forwarding=1 (if you use IPv6)
  • net.ipv4.conf.all.rp_filter=0 and net.ipv4.conf.default.rp_filter=0 (if asymmetric routing may occur)

Example /etc/wireguard/wg0.conf (hub):

[Interface] Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <hub_private_key>
SaveConfig = true
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -s 10.10.0.0/16 -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -s 10.10.0.0/16 -o eth0 -j MASQUERADE

Then add peer sections for each spoke (Site A and Site B):

[Peer: SiteA] PublicKey = <siteA_pub>
AllowedIPs = 10.10.1.0/24, 10.0.0.2/32
PersistentKeepalive = 25

[Peer: SiteB] PublicKey = <siteB_pub>
AllowedIPs = 10.10.2.0/24, 10.0.0.3/32
PersistentKeepalive = 25

Notes:

  • AllowedIPs advertises the remote LAN subnets to the hub so the hub will route traffic to those subnets via the WireGuard peers.
  • The hub’s PostUp uses iptables MASQUERADE to handle internet-bound traffic if you want hub to NAT spoke traffic — remove or adjust if not desired.

Spoke Configuration

On each spoke (site), configure wg0 and add a static route so that local LANs are reachable by the hub over WireGuard.

Example /etc/wireguard/wg0.conf (Site A):

[Interface] Address = 10.0.0.2/24
PrivateKey = <siteA_priv>
SaveConfig = true
PostUp = sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT

[Peer: Hub] PublicKey = <hub_pub>
Endpoint = hub.example.net:51820
AllowedIPs = 10.0.0.1/32, 10.10.2.0/24, 10.10.1.0/24
PersistentKeepalive = 25

Key points:

  • On the spoke, you must include the remote LAN(s) you want routed through the hub into AllowedIPs. If you only include the remote WireGuard addresses, the spoke will not send LAN-destined packets into the tunnel.
  • Include your own LAN (10.10.1.0/24) too if you plan to allow hub-originated traffic to reach the local subnet via the WG interface (this is often not necessary but sometimes used to ensure routes).

Routing Between LANs

There are two approaches to make sure that packets between LANs traverse the hub:

1. Route propagation (recommended for explicit control)

  • On the LAN router at each site, add a static route for the remote LAN(s) via the WireGuard peer (the spoke host) IP. For example, on Site A router add a route 10.10.2.0/24 via 10.10.1.10 (host that runs WireGuard inside LAN) or via the WireGuard host’s LAN IP.
  • This preserves original source addresses and avoids NAT.

2. NAT at the hub (simpler at first but masks source)

  • Hub NATs packets coming from spokes to other spokes (or to internet). This requires less route configuration on LAN routers but loses visibility of original IPs and complicates some services.
  • Use carefully for quick setups or when you can’t modify remote routers.

When possible, prefer route propagation (static or dynamic via a routing protocol like BGP/OSPF) to maintain transparent end-to-end connectivity.

Policy Routing and Multiple WANs

If the hub has multiple uplinks or you need to direct traffic from certain subnets out specific interfaces, use policy routing:

  • Create an IP rule per source subnet: ip rule add from 10.10.1.0/24 lookup 100
  • Populate the routing table 100 with routes: ip route add default via x.x.x.x table 100
  • Combine with iptables MARK to steer specific flows into policy-based routing.

This is useful for multi-tenant environments, per-customer routing, or compliance-driven routing policies.

Security Considerations

When routing between multiple subnets, consider the following security best practices:

  • Least privilege AllowedIPs: Only include prefixes that are required. Avoid broad 0.0.0.0/0 unless you intentionally want to route all traffic through the tunnel for that peer.
  • Firewall segmentation: Use iptables or nftables to restrict which subnets/services are reachable across the tunnel. WireGuard itself does not implement ACLs beyond AllowedIPs.
  • Authentication/Key rotation: Rotate keys periodically and have a secure process for distributing public keys to peers.
  • Logging and monitoring: Monitor connection metrics and packet flows. Use tools like wg show, tcpdump, and systemd journal to detect anomalies.

Troubleshooting Checklist

  • Check IP forwarding: sysctl net.ipv4.ip_forward should be 1.
  • Verify WireGuard status: wg show and wg showconf to inspect peers and AllowedIPs.
  • Confirm routing tables: ip route show and ip rule show for policy routing issues.
  • Test connectivity stepwise: ping the remote WireGuard address first, then the remote LAN gateway, then a host. Use tcpdump on wg0 and LAN interfaces to observe where packets are being dropped.
  • Inspect rp_filter settings: if asymmetric paths exist, set rp_filter to 0 for affected interfaces.
  • Check firewall chains: ensure FORWARD chain accepts the tunnel traffic and necessary NAT rules are present.

Scaling and Automation

For many sites or dynamic environments, consider automation:

  • Use configuration management tools (Ansible, Salt) to push WireGuard configs and keys.
  • Automate route updates. For dynamic topologies, run a routing protocol (FRRouting or bird) on the hub and spokes to exchange LAN prefixes. WireGuard + OSPF/BGP gives flexibility for large deployments.
  • Centralize key management using vault solutions or signed key distribution to avoid manual errors.

Advanced: BGP over WireGuard

In enterprise environments, pairing WireGuard with a dynamic routing protocol like BGP or OSPF provides scalable and resilient route exchange:

  • Run an iBGP session between hub and spokes over WireGuard tunnels. Advertise local LAN prefixes and let the router control best-path selection.
  • Ensure loop prevention and proper route filters so only intended prefixes are advertised.
  • Consider route reflectors or confederations if scaling beyond tens of sites.

Conclusion

Routing traffic between multiple subnets with WireGuard is straightforward in small setups but requires careful planning for medium and large networks. The keys to success are: enable and verify IP forwarding, define precise AllowedIPs, implement proper routing (static or dynamic), and secure traffic with firewall rules and key management. For production deployments, use policy routing and routing protocols where necessary, and automate configuration to reduce human error.

For more in-depth tutorials and production-ready examples, visit Dedicated-IP-VPN at https://dedicated-ip-vpn.com/. Dedicated-IP-VPN provides practical guidance and resources tailored to administrators and developers managing secure multi-site networks.