Self-hosted (combined image + reverse proxy): all peers stuck on Relayed, RemoteCandidate always 0 #6685
Replies: 2 comments 1 reply
|
Thanks for the detailed write-up! I’d focus on the Signal path first. A few concrete things I’d test: 1. Bypass Traefik for one test Expose the NetBird server directly on a temporary test hostname/port, or run a temporary clean quickstart instance without Traefik, then enroll two test peers against that URL. If P2P starts working there, the issue is almost certainly in the reverse proxy path. 2. Check the Traefik routing split For the combined server, make sure the gRPC paths go to the backend with - traefik.http.routers.netbird-grpc.rule=Host(`netbird.example.com`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`) || PathPrefix(`/management.ProxyService/`))
- traefik.http.services.netbird-grpc.loadbalancer.server.scheme=h2cAnd keep the normal HTTP/WebSocket paths separate: - traefik.http.routers.netbird-http.rule=Host(`netbird.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/oauth2`) || PathPrefix(`/`))The exact labels may differ, but the important part is that Signal gRPC is not accidentally handled by the plain HTTP/WebSocket router. 3. Confirm STUN is exposed outside Traefik From another machine: nc -uvz netbird.example.com 3478Or use any STUN test tool against: Traefik usually should not be in this path. UDP 3478 needs to be published on the host and allowed by the host/cloud firewall. 4. Check the Docker peer’s actual WireGuard port Inside the Docker client: netbird status --detailLook for the WireGuard listen port. Then confirm that exact UDP port is published and allowed. Publishing 51820/udp only helps if the client is actually listening on 51820. As a quick isolation test, run one routing peer with host networking: If P2P starts working with host networking, the problem is likely Docker port/NAT behavior on the routing peer, not NetBird Signal itself. 5. Capture one failed renegotiation On one affected client: netbird down
netbird up
netbird status --detailThen grab logs while SentOffer increases but RemoteCandidate stays 0: journalctl -u netbird --since "10 minutes ago" --no-pagerFor Docker clients: docker logs netbird-client --since 10mAnd on the server: docker logs netbird-server --since 10mI’d share redacted Traefik config, config.yaml, and those client/server logs. The interesting bit is whether candidate messages are being sent by one peer but never received by the other, or whether they are not being generated/sent at all. The restart behavior is the odd part. If direct connections briefly work after restarting netbird-server and then all pairs decay to relayed with RemoteCandidate: 0, that sounds worth investigating, but I’d first rule out the long-lived Signal gRPC stream through Traefik and the Docker routing peer’s UDP exposure. |
|
Hei thanks for the detailed answer. I‘m really at home, that’s why I haven‘t been able to answer.
|
Uh oh!
There was an error while loading. Please reload this page.
Before posting
Affected area
Peer connectivity, Relay / Signal / NAT traversal, Self-hosting / Deployment
Deployment type
Self-hosted - quickstart script
Operating system or environment
Linux, Android, iOS, Docker
NetBird version and upgrade status
Did this work before?
Not sure
Regression details
No response
Summary
All peer connections are permanently stock on
Relayedconnection type across all peers which are connected to the self-hosted instance.RemoteCandidatestays at0for every peer at every routing / exit node, on two independent hosts / providers, despite STUN working correctly and confirmed reachable. Traffic works (and functions correctly), but true P2P/ICE never establishes for any peer pair.Current behavior
P2P Connections won‘t establish.
Expected behavior
P2P connections between the peers.
Steps to reproduce
Deploy combined NetBird server (v0.74.2) behind Traefik v3, with a dedicated router forwarding /signalexchange.SignalExchange/* and /management.ManagementService/* via h2c (gRPC over cleartext HTTP/2).
Connect several peers: at least one routing/exit-node peer (netbird-client Docker image, bridge network mode, public IP, no NAT) plus regular peers on independent networks (mobile data, home WiFi).
Confirm Lazy Connections is off ("Lazy connection: false" in
netbird status --detail).Restart the netbird-server container. Peers briefly show P2P/Direct right after restart.
Wait a few minutes to hours, no further restarts.
Run
netbird status --detail: all peers now show Relayed, ICE candidate (Local/Remote): -/-. Debug state dump shows RemoteCandidate: 0 for every peer, RemoteAnswer: 1, SentOffer climbing (retry loop).Manually reconnect an affected peer (
netbird down && netbird up) — it reconnects fine but stays Relayed, RemoteCandidate still 0.STUN itself confirmed reachable/working throughout (manual STUN binding test succeeds). Result: once relayed, peers never recover P2P. Reproduced independently on two exit-node hosts on different providers/networks.
Environment and topology
netbirdio/netbird-server, management + signal + relay + STUN in one container), SQLite backend/signalexchange.SignalExchange/*and/management.ManagementService/*usingh2cscheme to the backend (gRPC over cleartext HTTP/2)/relay,/ws-proxy/,/api,/oauth2Self-hosted details, if available
No response
Logs, status output, or debug evidence
Related issues or discussions
No response
Impact
No response
Additional context
Symptoms
netbird status —-detailon both exit-node containers shows, for every peer, every time:Internal peer state dump (
client/internal/internal/state_dump.go) confirms this is not just a display issue:RemoteCandidate: 0for every single peer, constantly, on both independent exit-node hosts.SentOfferclimbs very high (60+) for peers with long uptime, indicating a stuck retry loop rather than a one-time negotiation failure.Traffic is fully functional over relay (WireGuard handshakes succeed, exit-node routing works, internet access via 0.0.0.0/0 route works), so this is purely an ICE/P2P establishment issue, not a connectivity outage.
What has been ruled out
XORMappedAddressresponse with the server‘s public IP. Server logs also show real STUN Binding requests/responses from multiple peer IPs during normal operation.RemoteCandidate: 0behaviour - this routes out a NAT-type/CGNAT explanation for at least those two peers.secHeadermiddleware from Traefik entrypoint (potential interference with the long-lived gRPC bidirectional stream) - no change.netbird status —detailconfirmsLazy connection: falseon all peers. No change.TrustedHTTPProxiesmisconfiguration: An earlier, separate issue (incorrect trusted proxy CIDR causing ALL peers to be relayed due to spoofed client IPs from Traefik) was found and fixed. After the fix, peers briefly archive P2P connections immediately after a fresh server restart, but degrade back toRelayed / RemoteCandidate: 0within minutes to hours, and stay that way through subsequent reconnects - suggesting this is a separate, second issue layered on top of the (now fixed) proxy-trust bug.Observed pattern
netbird-serverrestart, some peers briefly showP2P / Direct: true.Relayedand stay there - including after individual peer reconnects (netbird down && netbird up), which do not recover P2P.Question
Is there a known issue in 0.74.x (or a documented interaction with combined-image+reverse-proxy deployments) where ICE candidate messages specifically fail to propagate the Signal exchange after some period of uptime, while Offer/answer messages and STUN itself continue to work?
All reactions