You installed WireGuard on your VPS. You read three contradictory tutorials. The wg0.conf you pasted doesn't ping, or it pings but DNS leaks, or the kill switch nukes your entire machine when you disconnect. The cause is almost always the same: a template copied without understanding the original context.
This guide ships 8 production-tested wg0.conf templates running on Contabo VPS S, with use case, known gotchas, and the four lines you need to adapt. Pick the one that matches your scenario, swap keys, connect. Five minutes.
Shared conventions
Every template follows the same rules — simplify your mental model:
- Client subnet:
10.66.66.0/24(peers.2→.254) - Public server interface:
eth0(check withip route | awk '/default/ {print $5}') - UDP port:
51820(changeable, see template 7 obfuscation) - Keys: generated via
wg genkey | tee server.key | wg pubkey > server.pub - Sysctl:
net.ipv4.ip_forward=1enabled on the server (/etc/sysctl.conf) - MTU: 1420 by default, tuned when needed
PrivateKey blocks below are placeholders — replace with your own. Never commit these files to a public git repo.
Template 1 — Standard WireGuard server (1 roadwarrior client)
The most common case: your Contabo VPS exposes a tunnel for your MacBook on the road.
# /etc/wireguard/wg0.conf (server)
[Interface]
PrivateKey = SERVER_PRIVATE_KEY_HERE
Address = 10.66.66.1/24
ListenPort = 51820
MTU = 1420
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Eric's MacBook
PublicKey = CLIENT_PUBLIC_KEY_HERE
AllowedIPs = 10.66.66.2/32
Client side:
# /etc/wireguard/wg0.conf (macOS/Linux client)
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY_HERE
Address = 10.66.66.2/24
DNS = 9.9.9.9, 149.112.112.112
MTU = 1420
[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Watch points:
AllowedIPs = 0.0.0.0/0, ::/0routes ALL client traffic through the VPS — that's what you want for a classic VPN.PersistentKeepalive = 25is mandatory if the client sits behind NAT (hotel network, 4G) — without it, the tunnel dies after ~3 min of idle.DNS = 9.9.9.9forces Quad9 resolution — otherwise the client uses local DNS and your queries leak.
Template 2 — Home VPN (split-tunnel: local LAN excluded)
You want VPN for your web traffic but keep local LAN access (printer, NAS) without going through the tunnel.
# Home client
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY_HERE
Address = 10.66.66.2/24
DNS = 9.9.9.9
MTU = 1420
[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = vpn.example.com:51820
# Everything except RFC1918 LAN + multicast + APIPA
AllowedIPs = 0.0.0.0/1, 128.0.0.0/2, 192.0.0.0/3, 224.0.0.0/4, 240.0.0.0/5, 248.0.0.0/6, 252.0.0.0/7, 254.0.0.0/8, 255.0.0.0/9
PersistentKeepalive = 25
The fragmented subnet trick (instead of 0.0.0.0/0) makes WireGuard add specific routes that don't override the default route. Your 192.168.x.x and 10.x.x.x LAN stay reachable directly.
Online tools to generate these ranges: wg-allowed-ips.compute() or Tunsafe / WireGuard Network Calc.
Template 3 — Travel VPN with strict netfilter kill switch
You're flying to China, Iran, Russia. If the tunnel drops, nothing leaks. Solution: netfilter kill switch that drops everything except traffic via wg0 and the server handshake.
# Travel kill-switch client
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY_HERE
Address = 10.66.66.2/24
DNS = 9.9.9.9
MTU = 1280
PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PostUp = ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Those four PostUp/PreDown lines are the real kill switch — not an application popup. Until wg-quick down wg0 runs, no packet leaves the interface that isn't WireGuard-marked.
MTU 1280 for picky networks (hotels, 3G). Losing 1% throughput beats a flaky tunnel.
Template 4 — Multi-peer (5 devices, same user)
You have a MacBook, iPhone, iPad, work VPS, Raspberry Pi. You want them all routed via the same VPN with separate subnets for monitoring.
# /etc/wireguard/wg0.conf (server)
[Interface]
PrivateKey = SERVER_PRIVATE_KEY_HERE
Address = 10.66.66.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# MacBook
PublicKey = MAC_PUBKEY
AllowedIPs = 10.66.66.2/32
[Peer]
# iPhone
PublicKey = IPHONE_PUBKEY
AllowedIPs = 10.66.66.3/32
[Peer]
# iPad
PublicKey = IPAD_PUBKEY
AllowedIPs = 10.66.66.4/32
[Peer]
# Worker VPS (Hetzner)
PublicKey = WORKER_PUBKEY
AllowedIPs = 10.66.66.5/32
[Peer]
# Raspberry Pi home
PublicKey = RPI_PUBKEY
AllowedIPs = 10.66.66.6/32
Each peer holds a fixed IP on the 10.66.66.0/24 subnet. If you monitor via Prometheus (see the monitoring guide), you can label by IP and know who burns how much bandwidth.
Template 5 — Hub-and-spoke (peer-to-peer between clients)
By default WireGuard doesn't route between peers. If you want your iPhone to talk to your Raspberry Pi through the hub server:
On the server, add:
sysctl -w net.ipv4.ip_forward=1
And in server [Peer] blocks, set AllowedIPs = 10.66.66.X/32 (peer's unique IP).
On the clients, change AllowedIPs:
[Peer]
PublicKey = SERVER_PUBLIC_KEY_HERE
Endpoint = vpn.example.com:51820
# Whole subnet, not just 0.0.0.0/0
AllowedIPs = 10.66.66.0/24
Now from your iPhone (10.66.66.3) you can ssh pi@10.66.66.6 directly — the hub forwards.
Template 6 — Per-peer egress restriction (allowlist)
Advanced case: a peer (your kid, a guest, an automated service) should connect to the tunnel but only reach specific domains/IPs.
Add to the server PostUp, in addition to MASQUERADE:
iptables -A FORWARD -s 10.66.66.50/32 -d 1.1.1.1 -j ACCEPT
iptables -A FORWARD -s 10.66.66.50/32 -d 142.250.0.0/15 -j ACCEPT # Google
iptables -A FORWARD -s 10.66.66.50/32 -j REJECT
Peer 10.66.66.50 can only reach 1.1.1.1 (DNS) and Google IPs. Everything else is rejected.
Template 7 — Obfuscation on port 443 via udp2raw
Some corporate firewalls block outbound UDP. Solution: encapsulate WireGuard inside fake TCP/443 via udp2raw.
Server side:
udp2raw_amd64 -s -l 0.0.0.0:443 -r 127.0.0.1:51820 -k "p@ssw0rd_long" --raw-mode faketcp
Client side:
udp2raw_amd64 -c -l 127.0.0.1:50000 -r vpn.example.com:443 -k "p@ssw0rd_long" --raw-mode faketcp
In the client wg0.conf, change Endpoint:
Endpoint = 127.0.0.1:50000
Throughput drops (~30% overhead) but it tunnels through almost any corporate DPI.
Template 8 — Site-to-site (two LANs bridged)
Two houses, two Raspberry Pi gateways, one Contabo VPS as hub. Each LAN sees the other as if it were local.
Server (VPS hub):
[Interface]
PrivateKey = HUB_PRIVATE_KEY_HERE
Address = 10.66.66.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Site A (LAN 192.168.10.0/24)
PublicKey = SITE_A_PUBKEY
AllowedIPs = 10.66.66.10/32, 192.168.10.0/24
[Peer]
# Site B (LAN 192.168.20.0/24)
PublicKey = SITE_B_PUBKEY
AllowedIPs = 10.66.66.20/32, 192.168.20.0/24
Site A (Raspberry Pi):
[Interface]
PrivateKey = SITE_A_PRIVATE_KEY_HERE
Address = 10.66.66.10/24
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
[Peer]
PublicKey = HUB_PUBLIC_KEY_HERE
Endpoint = vpn.example.com:51820
AllowedIPs = 10.66.66.0/24, 192.168.20.0/24
PersistentKeepalive = 25
Don't forget ip_forward on both Raspberries and a static route on other LAN machines if they need to answer from the other site.
Quick diagnostic when nothing works
You pasted the template, you run wg-quick up wg0, it starts — but ping fails. Checklist in this order:
wg show: last handshake recent? If "never" → handshake KO, checkEndpoint, UDP port on server firewall (ufw statusoriptables -L).ip routeon client: route to VPN subnet present? If not → clientAllowedIPsborked.dig @9.9.9.9 ifconfig.me +shortfrom connected client: does it return the VPS IP? If not → MASQUERADE missing on server, orip_forward = 0.tcpdump -i wg0on server: do you see packets coming in? If yes but nothing exitseth0→ FORWARD/MASQUERADE rule missing.- MTU:
ping -M do -s 1400 1.1.1.1from client. If "Frag needed" → drop MTU inwg0.confto 1380 or 1280.
If you're starting from scratch and stuck, the Contabo step-by-step setup guide walks the entire install from initial SSH.
In production: what these templates skip
They're intentionally minimal. For long-term prod, plan:
- Regular backup of
/etc/wireguard/(losing the server key = regen all clients) - Key rotation every 12-18 months (procedure: generate new key, add parallel peer, migrate clients one by one, retire old peer)
- Monitoring via Prometheus +
wireguard_exporter(see monitoring guide) - Fail2ban on UDP port 51820 if you spot port scan attempts in
journalctl -u wg-quick@wg0 - Per-peer bandwidth limit via
tc(Linux traffic control) if a peer hogs throughput
And above all: never commit a real wg0.conf to a repo, even private. Keep them .gitignore'd or encrypt with age / sops.
Going deeper
If you want to understand why WireGuard outpaces OpenVPN at equal CPU, we ran a detailed technical comparison with real iperf3 benchmarks on Contabo, kernel module vs userspace, and iPhone battery profile.
And for the leak-stopper part, the Linux kill switch guide (iptables + systemd) shows how to guarantee zero leak even when WireGuard crashes.
Every template above is in active use here over 14 months on a Contabo VPS S at 4.99 €/mo (/go/contabo). Zero unplanned tunnel outage, ~2 pre-announced maintenance windows.
★ Datacenter Nuremberg GDPR · ✓ IPv4 dédiée incluse · 200+ Mbps garantis
Get Contabo30 jours satisfait ou remboursé→