Wheelhouse docs

WireGuard between two sites#

You will end up with two routers joined by a WireGuard tunnel, each reaching the other's LAN. The configuration is the same shape as a road-warrior tunnel; the differences are that both ends have a fixed endpoint, and both ends carry routes.

Before you start#

  • The operator role and a licence on both routers.
  • The WireGuard feature module installed on both.
  • A key pair per router: generate pki wireguard key-pair.
  • Non-overlapping LAN subnets. Two sites both on 192.168.1.0/24 cannot route to each other, and nothing in this guide fixes that.
  • At least one side reachable at a stable address. If both are behind changing addresses, dynamic DNS on one side is the minimum.

Worked example throughout:

Site ASite B
LAN192.0.2.0/24198.51.100.0/24
Public address203.0.113.10203.0.113.20
Tunnel address10.10.0.1/3010.10.0.2/30

Step 1 — The tunnel on each side#

Site A:

set interfaces wireguard wg0 port 51820
set interfaces wireguard wg0 address 10.10.0.1/30
set interfaces wireguard wg0 private-key <site A private key>
set interfaces wireguard wg0 description 'to site B'

Site B:

set interfaces wireguard wg0 port 51820
set interfaces wireguard wg0 address 10.10.0.2/30
set interfaces wireguard wg0 private-key <site B private key>
set interfaces wireguard wg0 description 'to site A'

A /30 is enough for a point-to-point link and makes it obvious that this tunnel is for two machines.

Step 2 — The peer on each side#

This is where a site-to-site peer differs from a laptop: it has an endpoint and a keepalive, and its allowed IPs include the far LAN.

Site A:

set interfaces wireguard wg0 peer siteB public-key <site B public key>
set interfaces wireguard wg0 peer siteB allowed-ips 10.10.0.2/32
set interfaces wireguard wg0 peer siteB allowed-ips 198.51.100.0/24
set interfaces wireguard wg0 peer siteB address 203.0.113.20
set interfaces wireguard wg0 peer siteB port 51820
set interfaces wireguard wg0 peer siteB persistent-keepalive 25

Site B: the mirror image, with A's key, A's LAN and A's address.

allowed-ips on a peer is both a filter and a route hint: traffic for those prefixes goes into the tunnel, and traffic arriving from that peer is accepted only if its source is in that list. Getting it wrong produces a tunnel that handshakes and drops traffic silently.

persistent-keepalive 25 keeps a NAT binding alive on a side that is behind NAT. It costs almost nothing and saves the "works for a minute, then stops" problem.

Step 3 — Routes#

If the far LAN is in the peer's allowed-ips, WireGuard installs a route for it. On a design where you want the route explicit — and you usually do, because it shows up on the Static routes page — add it:

on site A
set protocols static route 198.51.100.0/24 next-hop 10.10.0.2

Add a static route.

Step 4 — The firewall, on both sides#

The listener:

set firewall ipv4 input filter rule 100 action accept
set firewall ipv4 input filter rule 100 description 'wireguard from the far site'
set firewall ipv4 input filter rule 100 protocol udp
set firewall ipv4 input filter rule 100 destination port 51820
set firewall ipv4 input filter rule 100 source address 203.0.113.20

Scoping it to the far site's address is worth doing when that address is stable.

And the traffic, in both directions:

set firewall ipv4 forward filter rule 40 action accept
set firewall ipv4 forward filter rule 40 description 'site B to here'
set firewall ipv4 forward filter rule 40 inbound-interface name wg0

set firewall ipv4 forward filter rule 41 action accept
set firewall ipv4 forward filter rule 41 description 'here to site B'
set firewall ipv4 forward filter rule 41 outbound-interface name wg0

Step 5 — Do not masquerade#

A common mistake carried over from a road-warrior setup. If the source NAT rule on the uplink matches everything leaving the router, traffic destined for the tunnel can be translated before it gets there, and the far side sees your public address instead of your LAN. Check that the masquerade rule names the uplink interface as its outbound interface — which is what the Uplinks page writes — rather than matching on source alone. Masquerade behind each uplink.

Check it worked#

bash
# on either router
sudo wg show
show interfaces wireguard wg0
ping -c 3 10.10.0.2

# from a host on site A's LAN
ping -c 3 198.51.100.1
traceroute 198.51.100.1

wg show reports the last handshake and the bytes each way. A handshake with no bytes is an allowed-IPs or firewall problem; no handshake is a key, endpoint or port problem.

The WireGuard page shows configuration, not handshakes. Tunnels, peers and their allowed IPs are there; live handshake times and transfer counters are not, because they would need wg show exposed by the agent, which it does not do.

Undoing it#

delete interfaces wireguard wg0
delete protocols static route 198.51.100.0/24
delete firewall ipv4 input filter rule 100
delete firewall ipv4 forward filter rule 40
delete firewall ipv4 forward filter rule 41

on both sides. Deleting one side leaves the other retrying against a peer that is not there, which is harmless and noisy.

See also#


Checked against ui/src/pages/WireGuard.tsx · ui/src/pages/Wan.tsx · ui/src/pages/Firewall.tsx

Updated 2026-09-02 wireguard vpn site-to-site