CGNAT relay
Some internet connections never give you a public address you can forward a port to. Mobile data, 5G home broadband, and some fibre ISPs put you behind carrier-grade NAT (CGNAT), where many customers share one public IPv4 and no inbound port can reach you — automatic port-opening (PCP/NAT-PMP/UPnP) and manual port forwarding both have nothing to open.
The relay is the fallback that keeps these servers reachable. When your agent has no way to accept a direct connection, off-LAN clients reach it through the atreoLINK relay instead — without the relay ever being able to read your traffic.
A fallback, not the default path
Section titled “A fallback, not the default path”On your home Wi-Fi the app still talks to your server straight over the LAN. With a forwarded or auto-opened port, off-LAN clients connect directly to your public address. The relay steps in only for the connections that would otherwise fail.
When it activates
Section titled “When it activates”It’s on by default and fully automatic — there’s nothing to set up for the common case. The agent works out whether it has a usable public inbound path and only relays when it doesn’t:
- If automatic port-opening or a manual forward gives you a reachable public address, the agent advertises that and doesn’t use the relay.
- If the only address available is a CGNAT or private one (your public IP starts with
100.64.x.x,10.x.x.x, etc.), the agent detects it and relays automatically.
Only when the agent decides it needs the relay does it ask atreoLINK for authorisation, which atreoLINK issues on demand over the agent’s existing control connection — telling it which relay to use and how to prove it’s allowed. An agent that has a direct path never asks, so a relay slot is reserved only for the servers that actually use the relay. You don’t configure a relay host or port.
How a CGNAT server stays reachable
Section titled “How a CGNAT server stays reachable”CGNAT blocks unsolicited inbound connections, but it allows outbound ones and their return traffic — that’s the whole trick.
flowchart LR Client["Off-LAN client<br/>(app or WireGuard)"] Relay["atreoLINK relay<br/>(sees only ciphertext)"] Agent["Your server<br/>behind CGNAT"] Agent -. "1 — agent dials OUT and<br/>holds the link open" .-> Relay Client -- "2 — WireGuard UDP" --> Relay Relay -- "3 — back down the<br/>agent's open link" --> Agent
- The agent dials out. Because CGNAT permits outbound connections, the agent opens connections out to the relay and holds them open with regular keepalives. The relay never has to initiate a connection back to your server.
- A client speaks ordinary WireGuard to the relay’s public address. To the client it looks like any other WireGuard endpoint.
- The relay delivers those packets back down the agent’s already-open outbound link. A small forwarder on the agent hands them to its local WireGuard, and replies retrace the same path.
That forwarder is a blind byte-mover, not a second WireGuard: it holds no keys and never looks inside the packets. The relay, likewise, only ever sees encrypted WireGuard bytes plus its own routing framing.
End-to-end encryption is preserved
Section titled “End-to-end encryption is preserved”Routing through the relay does not weaken your tunnel. The WireGuard session is still negotiated directly between your device and your server; the relay is just a postbox in the middle.
- It forwards opaque WireGuard ciphertext and holds no WireGuard keys, so it cannot decrypt anything.
- It keeps no database and is stateless — restart it and clients simply re-establish.
- Authorisation is brokered by atreoLINK: the relay only serves a server that atreoLINK has vouched for and that proves it holds its own identity key. Clients aren’t separately authenticated by the relay — the end-to-end WireGuard handshake does that, and a bogus client just fails it.
A stable address
Section titled “A stable address”The first time your agent needs the relay it’s assigned a stable relay address, and that address doesn’t change when the agent reconnects or the relay restarts. Because it behaves like an ordinary WireGuard endpoint, a relayed server works not just with the atreoLINK apps but with plain/vanilla WireGuard clients too — a manual WireGuard config you exported from the dashboard keeps pointing at the right place.
Privacy: what the relay can and can’t see
Section titled “Privacy: what the relay can and can’t see”The relay is blind to your traffic — it can’t read app data or notification contents, exactly as atreoLINK itself can’t.
It is, however, a metadata-holding element of the data plane. While it carries your connection it can observe the client and server IP addresses involved, and the timing and volume of traffic — the kind of thing any network hop on the path sees. It can’t see what you’re doing, only that traffic is flowing. That’s a step beyond atreoLINK’s metadata-holding control plane, which is exactly why the relay is used only as a fallback for servers that can’t be reached any other way, and never for traffic that has a direct path.
Relayed connections share atreoLINK’s relay infrastructure, so a relayed server’s throughput may be throttled for fair use. Free-plan servers are throttled more heavily than paid plans, and relay use is subject to fair use — sustained or excessive use may be rate-limited, restricted, or cut off per the Acceptable Use Policy. Direct and LAN connections aren’t in the relay’s path and aren’t affected — one more reason a direct path is preferable when you can get one.
How to tell whether you’re being relayed
Section titled “How to tell whether you’re being relayed”- Agent logs. When the agent brings up a relay session it logs
relay: session ready endpoint=<host:port> ingest=<host:port>. If a NAT’d gateway maps a port but hands back a carrier-NAT/private address, it also warns that the address is carrier-NAT/private and it’s using the relay. - A push notification. On a phone paired to the server you’ll get a one-off notification titled “Using relay (carrier-grade NAT)” the first time the agent decides it must relay, so you’re not left guessing why direct connections never worked.
docker logs atreoagent | grep -i relayControlling the relay (agent operators)
Section titled “Controlling the relay (agent operators)”You never configure a relay host or port — the agent learns those automatically. A few agent settings tune the automatic behaviour:
| Setting | Effect |
|---|---|
RELAY_ENABLED / relay.enabled | On by default. Set to false to opt this agent out of relaying entirely — with no direct path it’ll simply be unreachable off-LAN. |
RELAY_FORCE / relay.force | Off by default. Keeps a relay session up even when a direct path exists, so a relay endpoint is always available to hand to clients (clients still prefer LAN/direct — the relay stays a fallback for them). Useful for offering a relay config alongside a direct one; not needed for normal use. |
ENDPOINT_IP / endpoint_ip | Tells the agent it already has a reachable public address, so it advertises that direct endpoint and won’t relay. Use it when you’ve manually forwarded the port or have a static public IP. |
See Environment variables and the config file for where these live.
See also
Section titled “See also”- Manual port forwarding — open a direct port yourself (preferred when your ISP allows it).
- Manual WireGuard configs — generate a config for a plain WireGuard device, and the relay/direct re-download caveat.
- Endpoint discovery — how atreoLINK learns and publishes your address.
- Encryption — the end-to-end encryption the relay can’t see through.
- Common issues — when clients can’t connect.
© 2026 atreoLABS. All rights reserved.
WireGuard is a registered trademark of Jason A. Donenfeld.

