WireGuard site-to-site VPN between office and cloud: the whole config
A site-to-site WireGuard tunnel is two peers, about twenty lines of config each, and IP forwarding switched on. Each side lists its own subnet and the remote subnet in AllowedIPs, the NAT side keeps a mapping open with PersistentKeepalive, and traffic between the networks rides UDP on a single port. No controller, no certificate authority, no client daemons.
I use this shape to bridge a small office network and a cloud VPS: file storage and printers stay in the office, the application and its databases run in the cloud, and neither side's internal traffic touches the public internet.
How is site-to-site different from a client VPN?#
A road-warrior setup has many clients connecting to one server, each pushing most or all of its traffic into the tunnel. Site-to-site inverts the shape: exactly two peers, each one speaking for a whole subnet. The interesting part is that AllowedIPs does double duty:
- it is the crypto key routing table: packets arriving with a source address outside the peer's
AllowedIPsare dropped on the spot; wg-quickalso installs a system route for everyAllowedIPsentry, so it decides which packets enter the tunnel at all.
Which means a typo in AllowedIPs does not produce an error message. It produces a silent black hole that you debug with ping and wg show, like everyone before you.
The complete configs#
Office side, behind a router doing NAT, subnet 192.168.187.0/24:
# /etc/wireguard/wg0.conf (office, 192.168.187.0/24)
[Interface]
Address = 10.7.0.2/24
PrivateKey = <office private key>
[Peer]
PublicKey = <cloud public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.7.0.0/24, 10.20.0.0/24
PersistentKeepalive = 25Cloud side, a VPS whose application subnet is 10.20.0.0/24:
# /etc/wireguard/wg0.conf (cloud VPS, app subnet 10.20.0.0/24)
[Interface]
Address = 10.7.0.1/24
ListenPort = 51820
PrivateKey = <cloud private key>
[Peer]
PublicKey = <office public key>
AllowedIPs = 10.7.0.2/32, 192.168.187.0/24Key pairs come from wg genkey and wg pubkey; the private key never leaves the machine it was generated on. Both sides then need packet forwarding:
sysctl -w net.ipv4.ip_forward=1 # persist in /etc/sysctl.d/
ufw route allow in on wg0 out on wg0 # or the nftables forward chain equivalentPersistentKeepalive = 25 belongs on the NAT side (the office here): it refreshes the router's UDP mapping every 25 seconds so the cloud side can reach office hosts at any time. On a VPS with a public address it does nothing and can stay off.
MTU: fix this before you debug anything else#
WireGuard adds roughly 60 bytes of overhead over IPv4 (80 over IPv6), so wg-quick defaults the interface MTU to 1420. On German DSL, where PPPoE squeezes frames to 1492 bytes, the classic symptom set appears: small pings pass, large transfers stall, TLS handshakes hang mid-flight. Set 1412 on the office side and test with:
ping -M do -s 1372 10.7.0.1If 1372 bytes pass with the do-not-fragment flag set, the MTU chain is sound. If not, lower the interface MTU in steps of 10 and retest. Guessing from application behavior costs an afternoon; this test costs ten seconds.
Verifying, and the first three failure modes#
wg show answers most questions: the latest handshake should be under two minutes old while traffic flows, and the transfer counters should tick.
- No handshake at all: one side cannot reach the other's UDP 51820. Check the VPS firewall first (
ufw allow 51820/udp), then any provider-level packet filters. - Handshake succeeds, traffic doesn't flow: almost always asymmetric
AllowedIPs. A peer drops packets whose source address is not inside itsAllowedIPsentry, so both subnets must appear on both sides in the right roles. - One subnet works, the other doesn't: forwarding is off on the peer that should route between interfaces, or the firewall's forward chain lacks the rule.
tcpdump settles the rest: tcpdump -ni udp port 51820 shows the encrypted envelopes moving. Seeing plaintext application traffic on that capture means something re-routed around the tunnel, which is a different and more urgent conversation.
When I pick WireGuard over the alternatives#
IPsec interoperates everywhere; OpenVPN survives hostile networks and odd proxies. For a fixed office-to-cloud link I still default to WireGuard: the config fits on one screen and gets reviewed like code, key rotation is a file swap plus a reload, and the kernel implementation pushes serious throughput on modest hardware. The official quickstart and the wg-quick man page cover the remaining surface, and there is not much remaining surface, which is the point.