Introduction: Why Routing Policies Matter for WireGuard Deployments

WireGuard has become the go-to VPN protocol for performance, security, and simplicity. However, when it comes to real-world deployments for businesses, hosting providers, and advanced users, a single tunnel model is often insufficient. You need the ability to route traffic selectively — to send some traffic through a WireGuard peer, other traffic through the default gateway, and yet other flows through different peers based on source, destination, or application. This is where routing policies become essential.

Fundamentals: How WireGuard Integrates with the Linux Routing Stack

WireGuard operates as a kernel network interface (typically named wg0, wg1, etc.) and uses the system routing table like any other interface. The core WireGuard configuration element that determines which IPs are reachable via a peer is the AllowedIPs setting. However, AllowedIPs only defines destinations that the peer claims to route; it does not, by itself, implement advanced policy decisions such as source-based routing, per-user routing, or multiple-table routing.

To achieve granular control, you combine WireGuard with the Linux kernel’s routing policy database (RPDB), ip rule/ip route commands, and packet marking via iptables/nftables. Together these tools allow:

  • Source-based routing (e.g., route traffic from 10.0.1.0/24 through wg0)
  • Per-application or per-user routing (via iptables UID match or cgroup classifiers)
  • Multi-WireGuard-peers and failover routing strategies

Key Concepts and Building Blocks

AllowedIPs

AllowedIPs in the WireGuard peer configuration is both a routing declaration and an access control mechanism. On Linux, WireGuard programs the route to the peer’s endpoint and uses AllowedIPs to decide which traffic is encrypted and sent to that peer. For split-tunnel setups, list only the subnets you want routed through the tunnel (for example, 10.8.0.0/24, 192.168.100.0/24).

Routing Tables and ip rule

The Linux RPDB allows multiple routing tables indexed by number/name. Use ip rule add to select a routing table based on packet attributes (source address, fwmark, incoming interface). Then configure the chosen table with ip route add. Typical workflow:

  • Create a table (e.g., table 100 named wg0)
  • Add rules: ip rule add from 10.0.1.0/24 table 100
  • Add routes: ip route add default dev wg0 table 100

Packet Marking and Firewall-Based Decisions

For decisions based on users or processes, use packet marking in nftables or iptables. Marked packets can be matched by ip rule via the fwmark selector. Example flow:

  • Mark packets with iptables: iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 0x1
  • Add rule referring to mark: ip rule add fwmark 0x1 table 200
  • Add route in table 200 that sends traffic to wg1

Configuration Patterns for Common Use Cases

1. Per-Subnet Routing (Site-to-Site)

When linking branch networks via WireGuard, use AllowedIPs to advertise remote subnets and static routes on both ends. Add ip rule entries only if you need to force specific source subnets to use the tunnel despite overlapping route tables. For most site-to-site setups, the default system table plus AllowedIPs is sufficient.

2. Split Tunnel for End-Users (Selective Internet over VPN)

To route only certain destinations or internal resources via WireGuard, set AllowedIPs to the internal networks and public IPs you want tunneled. Avoid using 0.0.0.0/0 unless you want full-tunnel behavior. On clients that require application-specific routing, combine the following:

  • Use iptables/nftables to mark traffic from particular UIDs or ports.
  • Use ip rule to steer marked packets into a routing table with default route via wg0.

3. Multi-Hop and Chained VPNs

Advanced setups might chain multiple WireGuard peers for added anonymity or policy separation. In this case:

  • Chain by routing one peer’s egress through another interface.
  • Carefully manage AllowedIPs to avoid circular routes and route leaks.
  • Use per-table routing to prevent conflicts: each hop gets its own table and rules.

Practical Examples (Commands Explained)

The following compact examples illustrate common tasks. Replace addresses, interfaces, and UIDs to suit your environment.

Example A — Source-Based Routing for a Subnet

Goal: Send all traffic from 10.0.1.0/24 out via wg0 while other hosts use the default gateway.

Steps:

  • Create table entry in /etc/iproute2/rt_tables: add “100 wg0”
  • Add rule: ip rule add from 10.0.1.0/24 table wg0
  • Add route: ip route add default dev wg0 table wg0
  • Ensure AllowedIPs on the server includes the remote subnet

Example B — Per-User VPN Using iptables and fwmark

Goal: Route traffic from system user with UID 1001 through wg1.

  • Mark packets in mangle table: iptables -t mangle -A OUTPUT -m owner –uid-owner 1001 -j MARK –set-mark 0x1
  • Allow mark to be used for routing: ip rule add fwmark 0x1 table 201
  • Populate table 201: ip route add default dev wg1 table 201
  • Persist rules via systemd-networkd, /etc/network/interfaces, or firewall scripts

WireGuard-Specific Caveats and Best Practices

MTU, MSS Clamping, and Fragmentation

WireGuard adds encapsulation overhead. Set an appropriate MTU on the wg interface (typically 1420–1424 for IPv4 over UDP) and use MSS clamping on the egress interface to avoid fragmentation. For example, use nftables or iptables to clamp TCP MSS to the path MTU minus overhead.

DNS and Split Tunneling

DNS leaks are a common source of privacy compromise. If you route only some traffic through WireGuard, ensure DNS queries for tunneled domains also go through the tunnel. For systems using systemd-resolved or resolvconf, update DNS settings when a peer is active or use per-namespace resolvers.

Keepalives, Dynamic Endpoints, and Roaming

For mobile clients behind NAT, set PersistentKeepalive to maintain NAT mappings. For servers with dynamic endpoints, consider using Dynamic DNS or orchestration that updates the peer Endpoint field. WireGuard rekeying and roaming are transparent, but ensure your routing and firewall logic tolerates endpoint changes.

Limitations of wg-quick

wg-quick is convenient but limited for complex policy routing. It can set up basic pre/post hooks, but for robust multi-table policies, prefer explicit ip rule/ip route scripting or use systemd-networkd, NetworkManager, or orchestration tools to manage routing lifecycles and persistence.

Troubleshooting Checklist

  • Is AllowedIPs too broad? A 0.0.0.0/0 or ::/0 will capture all traffic for that peer.
  • Check ip rule list and verify the order and priorities (use ip rule show).
  • Confirm packets are marked as expected using iptables -t mangle -L -v or nft list ruleset.
  • Verify routing tables with ip route show table X.
  • Use tcpdump on interfaces (wg0, eth0) to confirm encapsulated vs. decapsulated packets.
  • Watch for MTU issues: use ping with large packet sizes and DF flag to test path MTU.

Operational Considerations

In production environments, treat routing policies as part of your configuration management and monitoring strategy:

  • Store ip rule and ip route entries in your automation (Ansible, Terraform, Salt).
  • Log policy changes and monitor for route flaps or unexpected default route modifications.
  • Test failover scenarios: simulate a peer outage and validate fallback routing works.
  • Document which users, containers, or VMs are bound to each policy to simplify audits.

Conclusion

WireGuard provides the secure transport, but achieving fine-grained traffic control requires leveraging Linux’s routing policy capabilities, packet marking, and careful configuration of AllowedIPs. For site-to-site connectivity, rely mainly on AllowedIPs and conventional routes. For per-user, per-application, or multi-peer topologies, combine ip rule/ip route, firewall-based packet marking, and disciplined MTU/DNS handling to build resilient and auditable routing policies.

For implementation guides, reference examples, and managed dedicated-IP VPN solutions tailored to business use, visit Dedicated-IP-VPN.