Troubleshooting WireGuard on Google Cloud: Why Your VPN Clients Can’t Reach Your VMs

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 = 25 to 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 Endpoint entries stay valid.
  • Duplicate iptables rules: Repeated wg-quick up runs or failed starts can leave stale rules. Check with sudo iptables -S FORWARD and sudo 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: client AllowedIPs or routing is wrong.
  • On wg0 but 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

ProblemCauseFix
Can’t reach the WireGuard VM from the VPCTunnel subnet overlaps the VPC subnetUse a separate subnet such as 10.8.0.0/24
Client can’t reach other VMsMissing forwarding, NAT, or AllowedIPsEnable IP forwarding, add MASQUERADE, add the VPC range to client AllowedIPs
Forwarding stops after a restartip_forward not persistentPut it in /etc/sysctl.d/ and reload
SSH hangs or transfers stallMTU too highSet MTU = 1280 on both sides and add MSS clamping
Random dropsNAT timeout, shared config, changing IPAdd keepalive, use unique keys, reserve a static IP

Hopefully this saves you a few hours of debugging.

Leave a Comment