Affiliate disclosure — This article contains Contabo affiliate links. If you order a VPS through them, we earn a commission at no extra cost to you. Every command and config below is documented from official WireGuard sources and written to be reproducible on your own machines.
A site-to-site VPN joins two entire networks so they behave like one. Every host on the office LAN can reach every host on the home LAN — your NAS, a printer, a hypervisor, a database — without installing a VPN client on each device. WireGuard makes this lean and fast: a single UDP port, a handful of config lines, kernel-space crypto. This guide builds a working WireGuard site-to-site tunnel from scratch, then explains the three things people always get wrong: AllowedIPs, IP forwarding, and host routing.
We will use a concrete, common topology: Site A is a home or office LAN behind NAT (no usable public IP), and Site B is a VPS with a public IPv4 acting as the always-on hub. The exact same config also links two homes, two offices, or two data centres — only the subnets change.
Site-to-site vs road-warrior: what actually changes
If you have set up WireGuard for a laptop before, 90% of this is familiar. The mental shift is small but crucial:
- Road-warrior (remote access): one device joins the network. Its peer announces a single tunnel address —
AllowedIPs = 10.66.66.2/32. - Site-to-site: a whole network joins. The peer announces the LAN subnet behind it —
AllowedIPs = 10.66.66.2/32, 192.168.10.0/24— and the WireGuard box forwards for every host on that LAN.
Everything else — keys, the handshake, the UDP port — is identical. If your handshake never completes, the causes are the same as any tunnel; work through the WireGuard handshake fix list before blaming the site-to-site parts.

Plan the address space first (5 minutes that save hours)
Before touching a config, write down three ranges. Getting this wrong is the number-one cause of a tunnel that connects but carries no host traffic.
| Range | Example | Rule |
|---|---|---|
| Tunnel subnet | 10.66.66.0/24 | Private to the VPN, must not collide with either LAN |
| Site A LAN | 192.168.10.0/24 | The home/office network |
| Site B LAN | 192.168.20.0/24 | The remote network — must differ from Site A |
The hard rule: the two LANs must not overlap. If both sides currently use 192.168.1.0/24, renumber one of them now. A host cannot route to a remote subnet that looks identical to its own — it will treat the address as local and never hand the packet to WireGuard. If you are fuzzy on subnet maths, our what is a subnet explainer covers CIDR in plain terms.
Step 1 — Provision the hub (Site B, the VPS)
Site B needs a public IPv4 and an open UDP port. We use a Contabo Cloud VPS 10 (4 vCPU, 8 GB RAM, 75 GB NVMe) at €5.50/month on the 12-month term — overkill for a tunnel, comfortable if it also runs other services. If you don't have a VPS yet, grab the Contabo Cloud VPS 10; pick a region close to whichever site sends the most traffic. For a clean base install of WireGuard on it, follow the step-by-step Contabo + WireGuard setup, then come back here for the site-to-site routing.
Install WireGuard and generate keys on the VPS:
sudo apt update && sudo apt install -y wireguard
umask 077
wg genkey | tee siteB.key | wg pubkey > siteB.pub
Step 2 — Set up the WireGuard router at Site A
Site A's WireGuard box is any always-on Linux machine on the LAN — a Raspberry Pi, a small VM, an old laptop. It does not need a public IP; it dials out to the VPS. Generate its keys the same way:
wg genkey | tee siteA.key | wg pubkey > siteA.pub
This machine becomes the gateway for the remote subnet, so it must forward packets between its LAN interface and the WireGuard interface.
Step 3 — The two configs
Here is the whole tunnel. Note how each side's AllowedIPs lists the other site's LAN — that line is what turns a point-to-point link into a network-to-network link.
Site B — the VPS hub (/etc/wireguard/wg0.conf):
[Interface]
Address = 10.66.66.1/24
ListenPort = 51820
PrivateKey = <siteB.key>
# forward + masquerade so Site A hosts can also reach the internet via the VPS
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Site A
PublicKey = <siteA.pub>
AllowedIPs = 10.66.66.2/32, 192.168.10.0/24
Site A — the LAN router (/etc/wireguard/wg0.conf):
[Interface]
Address = 10.66.66.2/24
PrivateKey = <siteA.key>
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
[Peer]
# Site B (the VPS hub)
PublicKey = <siteB.pub>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.66.66.0/24, 192.168.20.0/24
PersistentKeepalive = 25
Two details that matter:
PersistentKeepalive = 25lives on the side behind NAT (Site A). It sends a tiny packet every 25 seconds so the home router keeps the UDP path open and the VPS can always reach back.Endpointonly appears on Site A's peer block — Site A dials the VPS. The VPS learns Site A's address when the first packet arrives, so its peer block has noEndpoint.
Bring both tunnels up with sudo wg-quick up wg0 and check sudo wg show. A latest handshake within the last two minutes on both sides means the link is alive.
Step 4 — The step everyone forgets: host routing
This is where most site-to-site tunnels stall. The routers can ping each other across 10.66.66.x, but a laptop on Site A still cannot reach a NAS on Site B. Why? Because the other hosts on each LAN have no idea the WireGuard box is the door to the remote subnet. They send the packet to their default gateway, which has no route for 192.168.20.0/24, and it dies.
You have two clean ways to fix it:
- Static route on the LAN gateway (best). On Site A's main router, add a static route: destination
192.168.20.0/24, gateway = the Site A WireGuard box's LAN IP (e.g.192.168.10.5). Do the mirror on Site B. Now every host inherits the route automatically. Most consumer and business routers support static routes in their admin panel. - Per-host static routes. If you cannot touch the router, add a route on each machine that needs cross-site access:
# on a Site A host, reach the Site B LAN via the local WireGuard box
sudo ip route add 192.168.20.0/24 via 192.168.10.5
If the WireGuard box is your LAN's default gateway, you can skip this entirely — it already forwards. This is conceptually a routing problem, not a VPN problem; the same logic as port forwarding but applied to a whole subnet instead of one port.
Step 5 — Firewall the forwarding path
Forwarding without a firewall rule means either nothing passes or everything does. Be explicit. On the VPS hub, the PostUp above already accepts forwarded traffic from wg0; tighten it if you only want specific subnets to talk:
# only allow Site A LAN <-> Site B LAN, drop the rest
iptables -A FORWARD -s 192.168.10.0/24 -d 192.168.20.0/24 -i wg0 -j ACCEPT
iptables -A FORWARD -s 192.168.20.0/24 -d 192.168.10.0/24 -o wg0 -j ACCEPT
Make the rules persistent (iptables-save, or netfilter-persistent save on Debian/Ubuntu) so they survive a reboot. The single UDP port also needs to be open in front of the VPS — see which port WireGuard uses and how to open it if the handshake never starts.
Verifying the full path
Run these in order and stop at the first failure:
- Tunnel up?
sudo wg showon both ends → recentlatest handshake, non-zerotransfer. - Router-to-router? From Site A's WireGuard box:
ping 10.66.66.1. Fails → it's a tunnel problem, not site-to-site. - Router to remote LAN? From Site A's box:
ping 192.168.20.1. Fails →AllowedIPsorip_forwardon Site B. - Host to remote host? From any Site A laptop:
ping 192.168.20.50. Fails here but step 3 worked → host routing (Step 4).
That four-line ladder isolates the exact layer that is broken in under a minute.
When to add more sites — hub-and-spoke
To link three or more networks, keep the VPS as a central hub and add each new LAN as a spoke peer on it. Each spoke lists all the other LAN subnets in its AllowedIPs (routed via the hub), and the hub lists each spoke's LAN. This stays simple up to a handful of sites. Past that — or if you want automatic key rotation, ACLs, and NAT traversal without a public IP on any side — a mesh control plane like Tailscale or Headscale is less work than hand-editing peers; weigh it in our Tailscale vs WireGuard self-host comparison.
Why self-host the hub instead of a managed service
A site-to-site link is exactly where owning the box pays off: no per-seat pricing, no data limits, no third party in the path between your two networks. A Contabo Cloud VPS 10 at €5.50/month gives you a public IPv4, full root, unlimited traffic (fair-use) and the freedom to open the one UDP port you need — the ideal always-on hub for tying your sites together. Build it once and it runs for years.
Going further
- Self-host VPN on Contabo: full WireGuard guide 2026
- WireGuard handshake did not complete: the full fix list
- What is a subnet? Subnetting and CIDR explained
- Port forwarding explained: reach a server behind your router
- Tailscale vs WireGuard self-host: which to choose
- Ready-to-use WireGuard config templates 2026
Sources and references:
- WireGuard whitepaper — Jason A. Donenfeld
- WireGuard quickstart and wg-quick docs
- wg(8) and wg-quick(8) man pages
- Arch Wiki — WireGuard (routing & site-to-site)
Published 2026-06-26. Topology validated on Debian 12 and Ubuntu 24.04 with a Contabo Cloud VPS 10 hub and a Raspberry Pi LAN router. Your subnets, router models and ISP NAT behaviour will differ — always confirm each layer with wg show, ping and ip route before considering a setup final.
Reminder: running WireGuard and self-hosting a VPN is legal in the EU, US, Canada and most democratic countries. VPNSmith publishes this content for educational purposes.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200+ Mbps guaranteed
Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region→
