A WireGuard test carrying a default route, a LAN prefix, DNS and a firewall exception cannot tell you which of them broke. Strip it to two peers, one address pair and one permitted service, and the failure has nowhere to hide. The boring baseline is the diagnostic. Build that first; it takes fifteen minutes and saves the afternoon you would otherwise spend guessing.

What the baseline proves is narrower than most people expect. It proves that two identities agree, that UDP reaches the listening port, and that one allowed destination carries a request. Forwarding, NAT, LAN routes and DNS are separate features to add afterwards, one at a time.

Keys, addresses and the single endpoint

Generate a key pair on each system and move only the public halves between them. wg genkey | tee privatekey | wg pubkey > publickey is the documented one-liner, and it leaves the private key in a file you can then protect. Choose tunnel addresses from a range that appears in no other configuration you own, such as 10.99.0.1/24 and 10.99.0.2/24. Write down which address belongs to which machine, because every later check depends on that mapping.

Private keys never travel. If a test needs one copied to the other side, the test has already gone wrong. Where the two systems sit behind NAT, at least one needs a reachable UDP port while the other carries an Endpoint pointing at it, and the public address and port belong in your notes rather than in your assumptions about 51820.

A port forward that maps a different external port works fine as long as the Endpoint matches it. Mismatches here produce silence rather than errors, which is why they are worth checking on paper before debugging on the wire.

The fields that belong to wg and the ones that do not

The core field set is small: PrivateKey and ListenPort under Interface, PublicKey, AllowedIPs and Endpoint under Peer. Address and DNS belong to wg-quick rather than to wg itself, and that distinction bites the moment the same configuration moves into another tool.

[Interface]
PrivateKey = <peer-a-private>
Address = 10.99.0.1/24
ListenPort = 51820

[Peer]
PublicKey = <peer-b-public>
AllowedIPs = 10.99.0.2/32
Endpoint = 203.0.113.10:51820

AllowedIPs carries two jobs at once. It selects which destinations go into the tunnel, and it authorises the peer to use those source addresses on arrival. Because of that, the mirror entry on the other side has to name your tunnel address exactly; a /32 for the wrong peer reads as a routing problem and behaves like an authorisation one. The WireGuard quickstart shows the same two-peer shape with the canonical wording if you want to compare field names.

Reading the handshake instead of the icon

Bring the interface up through whatever owns it, then read state rather than a status light. wg show prints the latest handshake per peer along with byte counters, and a handshake within the last few minutes means keys, endpoint and UDP path are all working. A counter that rises only in one direction is its own answer, and it is not the same failure as a handshake that never happens.

The two failures imply different investigations. No handshake at all points at key mismatch, a wrong Endpoint, blocked UDP or a firewall dropping the handshake itself. A handshake that succeeds while data goes nowhere points at AllowedIPs, a service that is not listening on the tunnel address, or a host firewall that accepts the port for the LAN but not for the tunnel interface. Those are different halves of the system, and the baseline separates them before you start changing rules.

Proving one permitted service, then the reverse

Make a request to the other peer's tunnel address on a service that is meant to answer, and choose something better than ping. A missing ICMP reply can be host filtering rather than a tunnel fault, so a silent ping proves neither direction. A TCP connection to a port you know is open, or a request that returns data, gives you an answer you can interpret.

Run the reverse direction as a separate test with its own result, because the two directions have separate firewall and AllowedIPs entries and can fail independently. If the handshake works and the request does not, the problem sits in the data path rather than the tunnel: AllowedIPs, the listening service, or a host firewall. Fix that before adding forwarding, NAT or DNS, or you will spend the afternoon debugging a feature that was never the fault.

Keeping the baseline as the reference

Save the working topology and the profile source in a place you can restore from, and keep it out of any directory that syncs to a public service. Then add one feature at a time, repeating the original request after each: forwarding, NAT, a LAN route, and only then DNS. Each addition that breaks the test is now identifiable, because nothing else moved.

A baseline that only turns an icon green has told you nothing about which addition caused the next failure. Keep the one permitted path working as the reference, and every later change gets measured against it.

Inyandiko zihuye