WireGuard: Not Connecting — Fix

The Problem

Your WireGuard tunnel won’t establish a connection. Symptoms include: no handshake between peers, the wg show command shows 0 bytes transferred, clients can’t reach the server, or the tunnel connects but internet access stops working.

The Cause

WireGuard connection failures are almost always caused by one of five things: firewall blocking UDP traffic, incorrect endpoint configuration, key mismatches between peers, NAT/port forwarding issues, or AllowedIPs misconfiguration.

The Fix

Method 1: Verify Port Is Open

WireGuard uses UDP (default port 51820). Check your firewall allows it:

# Check if WireGuard is listening
sudo ss -ulnp | grep 51820

# If using UFW
sudo ufw allow 51820/udp

# If using iptables
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT

Cloud VPS users: Also check the provider’s firewall panel (Hetzner Cloud Firewall, AWS Security Groups, DigitalOcean Firewall). These block traffic before it reaches your server’s iptables.

Test UDP connectivity from the client:

# On the server, start a temporary listener
nc -u -l 51820

# On the client, send a test packet
echo "test" | nc -u <server-ip> 51820

Method 2: Fix Handshake Failures

Run sudo wg show on both sides:

sudo wg show

If latest handshake is missing or shows (none), the peers aren’t communicating.

Check keys: Each peer’s PublicKey in one config must match the other peer’s PrivateKey-derived public key:

# On server: verify the client's public key matches
wg pubkey < client-private.key
# Output must match [Peer] PublicKey in server's wg0.conf

# On client: verify the server's public key matches
wg pubkey < server-private.key
# Output must match [Peer] PublicKey in client's wg0.conf

Common mistake: Swapping public and private keys. Each side uses its own private key in [Interface] and the other side’s public key in [Peer].

Method 3: Fix Endpoint Configuration

The client’s [Peer] section needs the server’s public IP and port:

[Peer]
PublicKey = <server-public-key>
Endpoint = <server-public-ip>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Behind NAT: If either side is behind NAT, add PersistentKeepalive = 25 to maintain the NAT mapping. Without this, the connection drops after the NAT timeout (usually 30-120 seconds).

Dynamic IP: If your server’s IP changes, use a DDNS hostname as the endpoint. WireGuard resolves the hostname once at startup — use a cron job to restart WireGuard if the IP changes, or use a tool like wireguard-vanity-address with dynamic DNS.

Method 4: Fix AllowedIPs

AllowedIPs controls both routing and cryptokey routing (which packets are accepted from which peer). Misconfiguration here is the most common cause of “connected but can’t reach anything.”

Full tunnel (all traffic through VPN):

# Client config
[Peer]
AllowedIPs = 0.0.0.0/0, ::/0

Split tunnel (only VPN subnet):

# Client config
[Peer]
AllowedIPs = 10.0.0.0/24

Server-side: Each peer’s AllowedIPs must include that peer’s VPN IP:

# Server config — for a client with VPN IP 10.0.0.2
[Peer]
AllowedIPs = 10.0.0.2/32

Method 5: Enable IP Forwarding and NAT

If the tunnel connects but clients can’t reach the internet through it, the server isn’t forwarding packets:

# Enable IP forwarding (temporary)
sudo sysctl -w net.ipv4.ip_forward=1

# Enable IP forwarding (permanent)
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Add NAT rules in your WireGuard server config:

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server-private-key>
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

Replace eth0 with your server’s actual network interface (check with ip route | grep default).

Method 6: Fix Docker-Specific Issues

If running WireGuard in Docker (e.g., linuxserver/wireguard):

# The container needs NET_ADMIN and SYS_MODULE capabilities
cap_add:
  - NET_ADMIN
  - SYS_MODULE

# And host networking or proper port mapping
ports:
  - 51820:51820/udp

# Sysctl must be set
sysctls:
  - net.ipv4.conf.all.src_valid_mark=1

Check that the kernel module is loaded on the host:

sudo modprobe wireguard
lsmod | grep wireguard

If wireguard module isn’t available, install it:

# Ubuntu/Debian
sudo apt install wireguard-tools

# CentOS/RHEL
sudo dnf install wireguard-tools

Prevention

  • Test after every config change — run sudo wg show on both sides to verify the handshake succeeds.
  • Use PersistentKeepalive = 25 on any peer behind NAT.
  • Always pin your WireGuard Docker image version — kernel module compatibility matters.
  • Document your IP addressing scheme — track which peer gets which IP in AllowedIPs.
  • Back up your keys — losing a private key means regenerating keys for all peers that used the corresponding public key.

Comments