July 26, 2026
I am setting up a local server at home to run openclaw, multica and ollama. It will sit behind my router, with no public IP reachable from outside. That is fine as long as I am on the same network, but it will not be fine once I want to reach it from my laptop while traveling. I have been looking at WireGuard to fix this ahead of time, which sent me back into the details of how UDP hole punching actually works.
The problem is NAT. Home routers translate the private addresses on the LAN into a single public IP, tracking the mapping in a table keyed by source and destination IP and port. Incoming connections that do not match an existing outbound mapping get dropped, which is exactly what keeps a home server from being reachable directly. The usual fix is port forwarding on the router, but that requires access to the router’s configuration, and it does not help at all if the ISP itself uses carrier-grade NAT, which is common on mobile connections and increasingly on residential ones too.
UDP hole punching is a way around this without touching router settings. Two peers, each behind their own NAT, learn each other’s public IP and port (typically through a third rendezvous server both peers can reach). They then send UDP packets to each other at roughly the same time. Each outbound packet creates a mapping in the sender’s own NAT table, so when the other peer’s packet arrives shortly after, it matches that mapping and gets let through, even though from the NAT’s point of view no prior “connection” to that specific peer existed. UDP has no handshake, so the trick relies entirely on the NAT device treating any recent outbound packet to an address as license to accept a reply from it. This works reliably against full-cone and restricted-cone NATs. It tends to fail against symmetric NATs, which assign a different external port for every distinct destination, making the port a peer learned useless once the other side tries to use it.
WireGuard itself does not do any of this. It is a UDP-based protocol, but it has no mechanism to discover a peer’s changing public endpoint or to coordinate a simultaneous punch. What it does have is PersistentKeepalive, which keeps an already-established NAT mapping alive by sending periodic packets, useful once a path exists but not for opening one in the first place. This is why tools like Tailscale exist on top of WireGuard: a coordination server exchanges candidate endpoints between peers, attempts the punch, and falls back to a relay when both ends are behind symmetric NAT and punching cannot work.
For my case, the home router should mostly behave as a well-behaved NAT, so a manual WireGuard setup might be enough on its own once the server is actually running. The variable is whatever network the laptop is on: hotel wifi, a mobile hotspot, an office network with its own restrictions. Once the server is up, I plan to start with plain WireGuard and note whether the laptop side needs anything more than that.