WireGuard has no concept of a connection. It has peers, public keys and allowed IPs, and that last field does double duty as both an access control list and a routing table. Understanding that one detail is most of what it takes to design a working mesh.
1. One interface, many peers
# /etc/wireguard/wg0.conf
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <server key>
[Peer]
# laptop
PublicKey = <laptop pubkey>
AllowedIPs = 10.10.0.2/32
[Peer]
# branch office router
PublicKey = <router pubkey>
AllowedIPs = 10.10.0.3/32, 192.168.50.0/24
The second peer advertises a whole subnet behind it. Traffic for 192.168.50.0/24 is encrypted to that peer and decrypted only if it arrives signed by that key — routing and authorisation in one line.
2. Turn on forwarding, then stop
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
No NAT is needed between sites that both have real subnets. Adding masquerade because a tutorial said so is what turns a debuggable network into one where every packet capture shows the wrong source address.
3. Keepalives are for NAT, not for health
PersistentKeepalive = 25
Set it on peers behind NAT so the mapping stays open. Setting it everywhere just adds traffic. To check a tunnel, read the handshake age:
sudo wg show wg0 latest-handshakes
Anything older than three minutes on an active peer means the path is broken, whatever the interface state claims.