The tunnel appears to work only after the server sends something first. Initiate from the client and nothing happens, wait a few seconds, try again and it succeeds. The usual diagnosis is a broken NAT, and the usual diagnosis is wrong. What changed is that one peer learned where the other one lives.

Endpoint knowledge, not reachability

A WireGuard peer will not start a handshake until it knows where to send it. The configuration supplies that as an endpoint of address and port, and the running state tracks the endpoint it currently believes in.

wg show prints both sides of that picture, and a peer with no endpoint in the output cannot initiate anything, however correct its public key and allowed prefixes are. Compare the client's view with the server's before changing any rule, because the two often disagree about who knows what.

How a peer learns an endpoint

WireGuard is deliberately quiet. When a correctly authenticated packet arrives from an address the peer did not expect, the implementation updates that peer's endpoint to the new source, which is how roaming works on a laptop that changes networks. That behaviour explains the direction dependence: if the server sends first and its packet reaches the client, the client learns the server's current address and can answer. Until then it has nowhere to send. The mechanism is authenticated roaming rather than a trust-on-first-use shortcut, because the packet had to be valid before the endpoint moved.

The tools documentation for wg describes that update, and it is worth reading once if you are going to design around it.

Stateful translation adds its own direction

Where UDP passes through a translating gateway, the gateway creates state for the first packet it sees. RFC 4787 describes that behaviour and the timeouts that eventually remove the mapping. An idle mapping expires, and an inbound packet from the far side is then dropped by the gateway before it reaches the host. A tunnel that worked all morning stops answering in the afternoon for exactly this reason.

This is a second and independent mechanism that produces the same directional symptom. Endpoint knowledge and translation state both have to be right, and fixing one does not fix the other.

Configure the direction you actually want

For a client that must start the connection, put the server's endpoint in the client's peer section and keep it there. Relying on the server to send first is relying on an accident of traffic order that will change on a quiet network. The configuration should state the intended direction, not discover it.

For a peer behind translation that must stay reachable, enable a persistent keepalive on that side. A keepalive every 25 seconds is the usual starting value, and its purpose is to hold the mapping open rather than to test the tunnel. Choose one side to carry it, because enabling it on both doubles the traffic and the log noise without improving the result.

Compare state before and after the trigger

Capture the two views at the moment it fails, then again after the server sends. The first command shows endpoints and handshake age. The second shows whether a translation entry exists for the port. The capture shows which direction actually crossed the wire, and the route check confirms the client is not sending that traffic somewhere else entirely.

wg show
conntrack -L -p udp 2>/dev/null | grep 51820
tcpdump -ni any -c 20 'udp port 51820'
ip route get 198.51.100.7

Record the endpoint column and the translation entry together. A peer with no endpoint and a gateway with no mapping are two different faults. Only one of them is fixed by a keepalive.

Allakkat attuumassuteqartut