Once the tunnel handshakes, three wg0.conf settings fix the usual real-world problems.
DNS — stop leaks on a full tunnel
On a client doing a full tunnel, set a DNS server so queries go through the tunnel, not your local resolver:
[Interface]
Address = 10.0.0.2/24
PrivateKey = CLIENT_PRIVATE_KEY
DNS = 1.1.1.1
wg-quick applies this via resolvconf or systemd-resolved — install one if DNS seems
ignored. Without it, a full tunnel can still leak DNS to your ISP.
PersistentKeepalive — stop idle drops behind NAT
A client behind NAT loses its mapping after ~30–180s of silence, so the server can’t reach it.
Add to the [Peer] on the side behind NAT (usually the client):
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = SERVER_PUBLIC_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
25 seconds is the standard value — small enough to keep any NAT mapping alive.
MTU — fix “handshakes but slow / stalls on big transfers”
The default WireGuard MTU is 1420. Over PPPoE, double-NAT, or some mobile networks, packets get
fragmented and large transfers stall. Lower it on the [Interface]:
[Interface]
MTU = 1280
1280 is a safe floor (the IPv6 minimum). Try 1380 first if you want to give up less throughput.
Verify
# DNS goes through the tunnel:
resolvectl status # or: cat /etc/resolv.conf
# big transfer no longer stalls after lowering MTU:
curl -o /dev/null https://speed.cloudflare.com/__down?bytes=100000000
Symptom → fix: DNS leaks → set DNS. Works then dies after a minute → PersistentKeepalive = 25. Pings fine but downloads hang → lower MTU. These three solve the large majority of “it
connects but…” reports.