All articles
connection-troubleshooting
2026-08-069 min read

WireGuard handshake did not complete

WireGuard logs handshake did not complete after 5 seconds and stops. The seven causes, in the order to check them, with the command for each.

Your client logs Handshake did not complete after 5 seconds, retrying (try 2) and then logs it again. Nothing else happens. A WireGuard handshake did not complete after 5 seconds because the peer never answered, and WireGuard does not tell you why. The interface exists, wg show lists the peer, and no traffic moves in either direction.

Seven separate faults produce that identical line. This page puts them in check order, cheapest first, with the command that discriminates each one.

Check wg show before you read any further. If it already reports a latest handshake and the transfer counters are moving, your tunnel is up and the problem is downstream. Read WireGuard handshake but no internet instead, because nothing on this page will help you.

What the message actually means

The handshake is the peer's first response, not your first transmission. WireGuard sends an initiation packet and waits five seconds for a response packet. No response means the initiation never arrived, or it arrived and the peer discarded it silently.

That silence is deliberate. WireGuard does not send error replies to unauthenticated packets, because doing so would confirm to a scanner that a server is listening. The design decision is sound and the diagnostic cost is real: a wrong key and a blocked port look identical from the client.

Read wg show before changing anything

Run wg show on the client before editing a single line of config. It is the only output that tells you which half of the connection is broken.

sudo wg show

You are reading four fields. endpoint is the address and port the client is actually sending to, which is not always the address you think you typed. latest handshake is absent entirely when no handshake has ever succeeded. transfer shows bytes sent and bytes received as a pair. allowed ips is the traffic the client will route into the tunnel.

The diagnostic value sits in the transfer pair. Sent bytes climbing with received bytes at zero means your initiation packets are leaving and nothing is coming back. Both at zero means the client is not transmitting at all, which points at the interface rather than the network.

The OPNsense poster in forum topic 30540 describes exactly this state: no latest handshake shown, and "the timestamps for the peer is at '0'." That is the signature of every cause below. It narrows nothing on its own.

Handshake did not complete after 5 seconds, cause by cause

Work down this list in order. Each check is cheaper than the one after it, and each one rules out a class of fault rather than a single misconfiguration.

The endpoint address or port is wrong

Confirm what the client is actually sending to. The Endpoint line in your config is resolved once, when the interface comes up, and wg show prints the result.

sudo wg show wg0 endpoints

Compare that against the Endpoint = value in the config file. A hostname that resolves to a stale address produces a silent failure with no log line of its own. Resolve it by hand and compare.

getent hosts vpn.example.com

Watch for the port as well as the address. WireGuard has no default port on the client side, and a peer stanza with no port is a config error rather than a fallback to 51820. Watch also for an endpoint pointing at a NAT router that no longer forwards the port.

UDP is blocked on this network

WireGuard is UDP only. It has no TCP mode, no fallback, and no way to notice that a network has dropped its packets rather than lost them.

Test the network rather than the tunnel. Bring up the same config on a phone hotspot. If the handshake completes there and fails on the original network, the config is correct and the network is filtering.

Confirm packets are leaving before you blame the far end.

sudo tcpdump -ni any udp port 51820

Do not trust nc -uz as a UDP reachability test. UDP has no handshake of its own, so a silent port and an open port produce the same result. If the network is the problem, hotel wifi blocking WireGuard covers what to change and what to switch to.

The public key does not match

This is the most common real cause, and it is invisible in every log on both sides. The server holds a PublicKey for your peer stanza. If that value is not the public key derived from the PrivateKey in your client config, the server discards your initiation without a word.

Derive the client's public key and read it back.

sudo wg show wg0 public-key

Compare it character for character with the PublicKey in the server's peer entry. A WireGuard key is 44 base64 characters and ends in =. Truncation during a copy is the usual mechanism, and a truncated key still looks like a key.

Check the direction as well. The PublicKey inside your client's [Peer] section is the server's key, not yours. Swapping those two values is a five-second mistake that costs an afternoon. Re-importing solves it faster than reading it: see importing your config, or take a clean file by regenerating a config.

The peer is behind NAT and needs PersistentKeepalive

A client behind NAT has no stable inbound path until it sends something outbound. The NAT mapping that lets the server reply expires after a period the client does not control, and once it expires the server's response packets have nowhere to go.

Add a keepalive to the client's [Peer] section.

PersistentKeepalive = 25

The value is seconds, and 25 is the figure the WireGuard project itself recommends for NAT traversal. Keepalive is disabled by default, so a config that does not name it is not using it. This matters most when the far end initiates, and least when your client is the only party that ever starts a handshake.

Read the [Peer] section of the config you were issued before you add the line. A supplied config may already carry a keepalive, in which case the interval is set and adding a second copy of the line only makes the file harder to read.

AllowedIPs on the server does not include your client address

AllowedIPs is a routing table and an access control list at the same time. On the client it selects what enters the tunnel. On the server it selects which source addresses are accepted from that peer.

If the server's peer stanza lists AllowedIPs = 10.8.0.5/32 and your client config sets Address = 10.8.0.6/24, the server rejects your traffic. It rejects it silently, which is why this cause survives so long.

You usually cannot inspect the server side on a commercial service. What you can do is confirm your client is using the address it was issued.

ip addr show wg0

Compare that against the Address = line in the config you downloaded. If you edited it, put it back. AllowedIPs and keepalive covers what the field controls in more detail.

The kernel module is missing or wg-quick cannot create the interface

This one arrives at the same symptom from a different direction. If wg-quick up cannot create the interface, you have no tunnel to hand shake over, and some clients report the timeout anyway.

The literal error is RTNETLINK answers: Operation not supported, printed immediately after the ip link add step.

sudo modprobe wireguard
lsmod | grep wireguard

An empty lsmod result means the module is not loaded. The usual causes are kernel headers that do not match the running kernel, a DKMS build that failed quietly, and Secure Boot refusing an unsigned module. Read the build failure rather than guessing.

sudo dmesg | tail -n 30

Read the failure in that output rather than matching it against a string copied from an article. The exact wording changes between kernel and iproute2 versions, so the line you get may not be the line anyone else quoted.

The system clock is wrong

WireGuard puts a timestamp inside the handshake initiation and uses it for replay protection. The responder records the greatest timestamp it has seen from each peer and discards any initiation that does not exceed it.

The practical consequence is narrow but real. A client clock that jumps backwards by a large amount will be refused until the clock passes the value the server already stored. A clock that is merely a few seconds out is not the problem.

timedatectl status

Check this last, because it is rare and because the two symptoms above are far more common. Do not spend time here until the key and the endpoint are both confirmed.

If the output reports the clock as synchronised, rule this cause out and return to the key and the endpoint.

Which check tells you which cause

Read the left column against what you have in front of you, then run the command in the right column.

What you observeMost likely causeCommand that confirms it
wg show prints no latest handshake, transfer is 0 B received, 0 B sentThe interface is not transmittingip link show wg0
Sent bytes climb, received stays at 0 BPackets leave and nothing returns: wrong key, wrong endpoint, or filtered UDPsudo tcpdump -ni any udp port 51820
Handshake works on a phone hotspot, fails on this networkUDP is filtered on this networkRetry on a second network
wg show wg0 endpoints prints an address you did not expectStale or wrong endpoint resolutiongetent hosts vpn.example.com
wg-quick up fails with RTNETLINK answers: Operation not supportedKernel module missing or unloadable`lsmod \grep wireguard`
Handshake succeeded once, then stopped after minutesNAT mapping expired, no keepaliveAdd PersistentKeepalive = 25
Handshake completes and counters move, nothing loadsNot this articleRead the no-internet page

One warning before you start changing things on a remote machine. Running wg-quick down wg0 over an SSH session that routes through that tunnel will drop the session. Bringing up a config with AllowedIPs = 0.0.0.0/0 on a remote server does the same thing for the same reason. Use a console, a second path, or a scheduled wg-quick down as a safety net.

When it connects and then stops after a while

A handshake that succeeds once and then times out is a different fault from one that never succeeds. The tunnel worked, so the key is right and the port is open. Something changed underneath.

The pattern is documented in angristan/wireguard-install issue #39. The poster reports it across three platforms at once:

"when disconnect in the client(iOS,Mac and windows), after some time to connect the wireGuard again, the status on the client side is success, but in the client log alway show 'handshake did not complete after 5 seconds retrying(try 2)'"

Three things produce that shape. The client roamed to a new network and its source address changed, so the server is still replying to the old one. The NAT mapping expired during idle time. Or the client suspended and resumed, and the interface came back without a fresh handshake.

Set PersistentKeepalive = 25 first, because it addresses two of the three. Bring the interface down and up as the confirmation step rather than as the fix.

sudo wg-quick down wg0 && sudo wg-quick up wg0

If the tunnel survives a reboot but not an hour of idling, keepalive is your answer. If it survives idling but not a network change, roaming is.

When the handshake succeeds and nothing loads

That is a different failure with a different check order. The tunnel is established, the counters move, and traffic still does not pass. The usual causes are MTU, AllowedIPs, and DNS, none of which touch the handshake at all.

Read WireGuard handshake but no internet for that case.

Quick answers

Why does the handshake fail only on mobile data?
Carrier networks apply carrier-grade NAT and sometimes filter outbound UDP on non-standard ports. The NixOS Discourse poster in thread 26237 hit this connecting from an iPhone on cellular data while the same config worked on wifi. Add PersistentKeepalive = 25 first. If that does not help, the carrier is filtering the port and you need a different endpoint port.
How can you tell if WireGuard is working?
Run wg show and read two fields. A latest handshake timestamp within the last two minutes means the peer is answering. Transfer counters increasing in both directions mean traffic is passing. Both present is a working tunnel. A handshake with no received bytes means the tunnel is up but something downstream is broken.
How do you troubleshoot WireGuard?
Start with wg show, not with the config file. It tells you whether the failure is at the handshake or after it, and those two have no causes in common. If there is no handshake, work down the seven causes on this page in order. If there is a handshake, the problem is MTU, routing or DNS.
Can WireGuard be blocked?
Yes, and easily. WireGuard runs over UDP with no TCP fallback, so any network that drops UDP to arbitrary ports stops it dead. Hotel, airport and corporate networks commonly do this. The block is indistinguishable from packet loss at the client, which is why a second network is the fastest test.
What causes a VPN to go down?
Four things do it for WireGuard specifically. An expired NAT mapping with no keepalive configured. A roaming client whose source address changed. A rotated key on the server side. An endpoint that moved. The first two are client-side and you can fix them yourself. The last two need a fresh config file from the provider. Work through connection troubleshooting for the other symptoms in this cluster. If you are setting the tunnel up for the first time, start at installing WireGuard.
Put it into practice

A 24-hour PulseVPN trial takes about a minute to set up and asks for no card, no email, and no name.

Start free trial

Related reading