Your edge servers will almost certainly spend their working life on networks you don’t control. You won’t get a static IP, and asking a customer’s IT team to open a port is a fast track to a lengthy change-management review that usually ends in a “no.”

In practice, you can only count on one reliable behavior: connections initiated from inside your edge server going out.

To keep things reliable at scale, we use a single rule: every site runs a WireGuard client that dials out to a central hub. The hub simply listens and never tries to initiate a dial back.

This post covers how to configure this setup, why common alternatives fall apart in enterprise settings, and what this rule buys your operational sanity. It builds on our previous pieces on updating robots in the field and our reference architecture.

The Reality of Customer NATs

To understand why we do this, look at how standard enterprise routers handle outbound traffic:

  1. When an edge machine sends a packet to the internet, the router rewrites the source address to its own public IP, assigns a temporary port, and remembers the mapping in a NAT table.
  2. When a response comes back matching that table entry, the router forwards it to the edge machine.
  3. If a packet arrives from the outside without an existing NAT table entry, the router silently drops it.

This design gives us our operational boundary: replies to sessions our server starts will get through; everything else gets blocked.

Designing around this constraint is also a huge selling point for corporate IT teams. An edge server that only makes outbound connections is invisible from the outside. Port scans reveal nothing because the server listens on zero public ports. Your entire footprint on their network is just a standard DHCP lease.

Even if a cooperative customer offers you a port forward or a local VPN endpoint, avoid taking it. Accepting one-off network tweaks splits your fleet into two operational models and leaves your deployment timelines at the mercy of individual IT approvals. Standardizing on outbound-only connections everywhere keeps the fleet uniform.

Configuring the One-Way Tunnel

We establish connectivity using WireGuard with two minimal configuration files.

1. The Central Hub

The hub listens on a static UDP port and expects connections from registered edge peers:

# /etc/wireguard/wg0.conf (Hub)
[Interface]
Address = 10.77.0.1/24
ListenPort = 51820
PrivateKey = <hub private key>

[Peer]
PublicKey = <edge public key>
AllowedIPs = 10.77.0.2/32

2. The Edge Server

The edge site holds its own key pair and targets the hub’s public address:

# /etc/wireguard/wg0.conf (Edge Server)
[Interface]
Address = 10.77.0.2/24
PrivateKey = <edge private key>

[Peer]
PublicKey = <hub public key>
Endpoint = hub.example.com:51820
AllowedIPs = 10.77.0.0/24
PersistentKeepalive = 25

How the Tunnel Opens

When the edge interface starts, it sends an initial packet to hub.example.com. The customer’s router translates this traffic and creates a NAT entry.

When the hub receives the handshake, it dynamically learns where the edge site lives based on the router’s public IP and translated port. Running wg show on the hub illustrates this:

peer: <edge public key>
  endpoint: 203.0.113.44:41832
  allowed ips: 10.77.0.2/32
  latest handshake: 9 seconds ago
  transfer: 1.1 KiB received, 4.6 KiB sent

The critical setting here is PersistentKeepalive = 25. Most firewalls and routers drop idle UDP mappings after 30 to 120 seconds of inactivity. Sending a tiny heartbeat packet every 25 seconds keeps the NAT table entry alive indefinitely.

If the customer’s router reboots or drops the mapping, the edge machine automatically re-establishes it on the next keepalive cycle.

Once this tunnel is up, all fleet traffic runs inside it: OS updates, container pulls, metrics collection, telemetry streams, and emergency SSH access.

the customer's networkno rule gets opened for youEdge server10.77.0.2no inbound listenerCustomerrouterNAT outwardHub10.77.0.1listens on UDP 51820never dials a sitethe site dials out, UDP 51820the NAT mapping carries every replynothing dials inkeepalive every 25 sholds the mapping open
blog.bensoussan.de
Figure 1. The edge server initiates the outbound connection across the customer's NAT. The hub receives the handshake, notes the translated endpoint, and communicates back over the established path without ever dialing in directly.

Operating Outbound Tunnels at Scale

  • Monitoring via handshakes: Because WireGuard updates the latest handshake timestamp for every active peer, you get built-in health checks. If a site’s handshake is older than a few minutes, it has lost power or network access.
  • Self-healing: If a remote site goes offline, you don’t need to intervene. As soon as power or connectivity returns, the local service restarts, sends a keepalive packet, and restores the tunnel.
  • Key rotation: Rotating security keys doesn’t require customer involvement. You can issue a new key pair via your deployment tool, update the hub config, and cleanly cut over without losing the site’s overlay IP.
  • Zero-touch onboarding: Adding a 50th or 100th site uses the same process as the first: generate keys, deploy the edge config, and power on the box.

Why Other Approaches Fall Short

When setting up edge infrastructure, three alternative architectures usually come up. Here is why we pass on them:

1. Inbound Port Forwards & Site VPNs

  • The problem: they require custom configuration on the customer’s network hardware.
  • Why it fails: enterprise security teams routinely reject port-forwarding requests. Requesting them introduces massive deployment delays and creates a fragmented architecture where every site has custom networking rules.

2. Mesh Overlays (Tailscale, Nebula, ZeroTier)

  • The problem: they add unnecessary control-plane dependencies.
  • Why it fails: mesh tools are great for peer-to-peer developer networks, but edge fleets primarily use a hub-and-spoke model (robots talk to central telemetry and deployment servers). Adding a mesh overlay introduces external coordination servers or extra PKI management layer on top of infrastructure you already run. When a site loses connectivity, it adds another variable to debug: Is the tunnel down, or is the mesh coordination server failing?

3. Reverse Proxies per Site

  • The problem: they only solve the problem for HTTP/WebSockets.
  • Why it fails: fleet infrastructure uses many non-HTTP protocols, such as ROS2/DDS traffic, metrics scraping, container registry syncs, and raw SSH. Running protocol-specific proxies for each service adds unnecessary complexity while still requiring an inbound path to reach the proxy in the first place.

Summary

By making outbound-initiated tunnels your primary connectivity rule:

  1. Security simplifies: sites don’t listen on public ports, leaving zero attack surface for external port scans.
  2. Deployments scale: onboarding a new customer requires no network configuration changes on their end.
  3. Operations stay clean: you manage a single WireGuard tunnel per site with clear, predictable state metrics.

I’m David Bensoussan. I build deployment and update infrastructure for hardware and robot fleets. If you’re working out how to ship software to machines you can’t reach, book 30 minutes and we can work out your fleet’s update path.

Try It Yourself

You can test this setup locally using the 06-outbound-wireguard environment in the robot-fleet-lab repository.

Running vagrant up boots three local VMs: a central hub, a simulated customer firewall with strict NAT rules, and an edge site. The included demonstration script (scripts/demo.sh) proves that the hub can reach the edge site through the outbound tunnel even though inbound connections to the edge network are blocked.