WireGuard is fast and simple, but running it on a Google Cloud VM has a few traps. A VPN that connects without errors can still fail to pass traffic to the other VMs in the VPC, or work for ping and then hang on SSH.
This post covers the issues I ran into, in the order you’re likely to hit them, with a fix for each.
1. VMs in the Same VPC Can’t Reach the WireGuard VM
Symptom: VMs in the same VPC can ping and SSH each other, but not the WireGuard VM.
Before blaming the firewall, check which interface the kernel uses to reach a neighbouring VM:
ip route get <vm-internal-ip>
If the output says dev wg0 instead of your normal interface (ens4, enp0s0, etc.), you have a routing conflict.
Cause: The wg0 interface was given an address in the same range as the VPC subnet. When WireGuard starts, the kernel adds a route for that range through wg0, and it overrides the real VPC route.
Fix: Give WireGuard its own subnet that doesn’t overlap the VPC:
[Interface]
Address = 10.8.0.1/24
Update the client configs to match, then restart with sudo wg-quick down wg0 && sudo wg-quick up wg0. A similar problem occurs if a server-side peer’s AllowedIPs contains the VPC range or 0.0.0.0/0. Keep server-side AllowedIPs to each client’s tunnel IP only (for example 10.8.0.2/32).
2. VPN Clients Can’t Reach Other VMs Through the Tunnel
Reaching the WireGuard server is different from reaching other VMs through it. For that, the WireGuard VM must act as a router. All of the following must be in place.
Client AllowedIPs must include the VPC range:
[Peer]
AllowedIPs = 10.8.0.0/24, 10.128.0.0/20
Replace 10.128.0.0/20 with your own subnet range. Without it, the client never sends that traffic into the tunnel.
IP forwarding must be enabled in the OS:
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
NAT and forwarding rules in wg0.conf:
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o <interface> -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o <interface> -j MASQUERADE
Replace <interface> with the real interface name from ip -br a. Each of these must be a single line. Masquerading makes the destination VMs see traffic from the WireGuard VM’s own internal IP, so your existing VPC firewall rules keep working.
“Can IP forward” must be enabled on the GCP VM:
gcloud compute instances describe <vm-name> --zone <zone> --format="value(canIpForward)"
If this returns False, GCP drops forwarded packets regardless of what iptables says. This setting can only be chosen when the VM is created, so you would need to recreate the VM with --can-ip-forward.
3. It Worked, Then Stopped After a Restart
Symptom: The handshake succeeds, but no traffic passes after a reboot or other change.
Cause: net.ipv4.ip_forward was off in the running kernel. If the setting only lives in a file that isn’t loaded at boot, or another file in /etc/sysctl.d/ sets it to 0 later in the load order, forwarding silently turns off.
Fix: Confirm the value and where it’s set:
sysctl net.ipv4.ip_forward
grep -r ip_forward /etc/sysctl.conf /etc/sysctl.d/
Keep it in a dedicated file as shown above, remove any conflicting entry, and reload with sudo sysctl --system. Then reboot once to confirm it survives.
4. Ping Works, but SSH Hangs on a Black Screen
Symptom: The tunnel is up and ping works, but SSH or PuTTY connects and shows a blank screen, or large transfers stall.
Cause: MTU. A GCP VPC uses an MTU of 1460, and WireGuard adds its own overhead, so large packets (like those in the SSH key exchange) get dropped or fragmented. Small packets pass, so ping looks fine.
Test it from the client with a large payload:
ping -f -l 1300 <vm-internal-ip> # Windows
ping -M do -s 1300 <vm-internal-ip> # Linux / Mac
If small sizes pass and large ones fail, it’s MTU.
Fix: Set a lower MTU in the [Interface] section on both the server and the client:
[Interface]
MTU = 1280
1280 is a safe starting value. Once everything works, you can try raising it (for example to 1380) and testing again. Also add MSS clamping on the server so TCP connections negotiate packet sizes that fit:
PostUp = ...existing rules...; iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
PostDown = ...existing rules...; iptables -t mangle -D FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
(...existing rules... stands for your current iptables commands. Don’t paste it literally.)
Restart the tunnel on both sides after the change.
5. Intermittent Drops
If the connection works but keeps dropping, check these:
- Missing keepalive: If the client is behind NAT (home router, mobile, office network), the NAT mapping expires when the tunnel is idle. Add
PersistentKeepalive = 25to the client’s[Peer]section. - Same config on two devices: If two devices share one key and tunnel IP, the server keeps switching the peer’s endpoint between them and each breaks the other. Give every device its own keypair and IP.
- Changing public IP: An ephemeral external IP changes when the VM is stopped and started. Reserve a static external IP so client
Endpointentries stay valid. - Duplicate iptables rules: Repeated
wg-quick upruns or failed starts can leave stale rules. Check withsudo iptables -S FORWARDandsudo iptables -t nat -S POSTROUTING, and clean up with a full down/up or a reboot. - Small instances: On e2-micro or e2-small, CPU throttling can cause drops. Check the CPU graph in the console.
6. The Whole Tunnel Goes Down After a Config Edit
If WireGuard stops working right after you edit wg0.conf, run it manually and read the error:
sudo wg-quick down wg0
sudo wg-quick up wg0
sudo journalctl -u wg-quick@wg0 -n 30 --no-pager
Common causes are a typo, a PostUp command split across multiple lines, or placeholder text accidentally pasted into the file. If PostUp fails, wg-quick aborts and tears the interface down. Revert to a minimal working config, then add changes back one at a time.
A Quick Debugging Checklist
When traffic from a client doesn’t reach a VM behind WireGuard, run these on the server while pinging from the client:
sudo wg show # is there a recent handshake?
sudo tcpdump -ni wg0 icmp # does traffic arrive from the tunnel?
sudo tcpdump -ni <interface> icmp # does it leave toward the VPC?
- Nothing on
wg0: clientAllowedIPsor routing is wrong. - On
wg0but not on the interface: forwarding is off or iptables is blocking it. - On the interface but no reply: a VPC firewall rule or the destination VM’s firewall is blocking it.
- Replies on the interface but not back on
wg0: NAT is missing or “Can IP forward” is off.
Summary
| Problem | Cause | Fix |
|---|---|---|
| Can’t reach the WireGuard VM from the VPC | Tunnel subnet overlaps the VPC subnet | Use a separate subnet such as 10.8.0.0/24 |
| Client can’t reach other VMs | Missing forwarding, NAT, or AllowedIPs | Enable IP forwarding, add MASQUERADE, add the VPC range to client AllowedIPs |
| Forwarding stops after a restart | ip_forward not persistent | Put it in /etc/sysctl.d/ and reload |
| SSH hangs or transfers stall | MTU too high | Set MTU = 1280 on both sides and add MSS clamping |
| Random drops | NAT timeout, shared config, changing IP | Add keepalive, use unique keys, reserve a static IP |
Hopefully this saves you a few hours of debugging.