Hi everyone,
I’ve been debugging an intermittent WebSocket disconnection issue for the past few days and I’ve narrowed it down to the 6PN internal mesh.
My app has an admin panel that should not be publicly accessible, so I access it via WireGuard VPN using the .internal address. The problem is that WebSocket connections over this path are extremely unstable — they drop every 4–36 seconds, making any real-time feature (Phoenix LiveView in my case) essentially unusable through the VPN. The same app works perfectly when accessed via Fly Proxy (HTTPS) or fly proxy.
I’m sharing detailed findings here in case this is a known issue or someone from the Fly team can take a look.
Summary
Long-lived TCP connections (WebSocket) to a Fly app via WireGuard + 6PN internal addresses (.internal:8080) experience random disconnections every 4–36 seconds. The same app works perfectly when accessed via Fly Proxy (HTTPS) or fly proxy (TCP tunnel). The issue is reproducible and isolated to the 6PN network path.
Environment
- App: single machine, shared-cpu-1x, 1GB RAM, London (lhr)
- Runtime: Elixir/Phoenix LiveView on Bandit HTTP server
- Client: macOS 15, WireGuard app (App Store), utun8 interface
- WireGuard peer: Fly gateway (created via fly wireguard create)
- Access: http://.internal:8080 (plain HTTP, no TLS)
Symptoms
Phoenix LiveView uses WebSocket for real-time UI updates. When accessed via 6PN:
- WebSocket connects successfully
- Server sends 1-second timer updates (tiny JSON diffs, ~50–100 bytes each)
- After 4–36 seconds, the client stops receiving updates (page freezes)
- Server-side, the process keeps sending — diffs accumulate in the TCP write buffer
- After ~15s heartbeat timeout, the server detects the dead connection and closes it
- Client attempts WebSocket reconnection, fails for ~10s, falls back to HTTP long-polling
Controlled experiment
I created an identical test page (/ht) that increments a counter every 1 second. I tested three network paths to the same app, same machine, same page:
┌────────────────────────────────────────────────┬──────────────────┬──────────────────────────────────────────┐
│ Path │ Transport │ Result │
├────────────────────────────────────────────────┼──────────────────┼──────────────────────────────────────────┤
│ https://myapp.example.com/ht │ Fly Proxy │ Stable — 70+ ticks, zero drops │
│ │ (HTTPS) │ │
├────────────────────────────────────────────────┼──────────────────┼──────────────────────────────────────────┤
│ http://localhost:8081/ht via fly proxy │ TCP tunnel │ Stable — 100+ ticks, zero drops │
│ 8081:8080 │ │ │
├────────────────────────────────────────────────┼──────────────────┼──────────────────────────────────────────┤
│ http://<app>.internal:8080/ht via WireGuard + │ 6PN mesh │ Broken — freezes after 4–36 ticks, every │
│ 6PN │ │ time │
└────────────────────────────────────────────────┴──────────────────┴──────────────────────────────────────────┘
The test was run with both paths open simultaneously. The HTTPS/fly-proxy connections remained stable while the 6PN connection dropped multiple times in the same time window.
MTU investigation
ICMP tests over the 6PN path reveal packet loss at larger sizes:
$ ping6 -c 3 -s 1400 fdaa:0:xxxx:xxx:xxx:0:a:302
3 packets transmitted, 0 packets received, 100.0% packet loss
$ ping6 -c 3 -s 1350 fdaa:0:xxxx:xxx:xxx:0:a:302
3 packets transmitted, 2 packets received, 33.3% packet loss
$ ping6 -c 3 -s 1200 fdaa:0:xxxx:xxx:xxx:0:a:302
3 packets transmitted, 3 packets received, 0.0% packet loss
The WireGuard interface MTU is 1420 (default), but the effective path MTU through 6PN appears to be ~1340 bytes.
Reducing the client interface MTU to 1280 (sudo ifconfig utun8 mtu 1280) did not fix the WebSocket drops — the connection still died after 16 seconds. So the MTU issue is real but there is also a separate connection-dropping problem.
What I’ve ruled out
- Application bug: server-side process stays alive and keeps processing; the freeze is at transport level only
- Bandit/Phoenix: same code works perfectly via Fly Proxy and fly proxy
- WireGuard tunnel itself: fly proxy also uses WireGuard but works fine (it bypasses 6PN routing)
- MTU alone: reducing MTU to 1280 (IPv6 minimum) didn’t fix it
- Traffic volume: a completely static page (no server push) also disconnects via 6PN
Expected behavior
WebSocket connections over 6PN should be as reliable as those via Fly Proxy or fly proxy, especially for lightweight traffic (< 100 bytes/second).
Workaround
Using fly proxy 8081:8080 -a and accessing via http://localhost:8081 instead of http://.internal:8080.
This works perfectly but adds an extra step (and it is not feasible on smartphones).
Any insights would be appreciated. Happy to provide more logs or run additional tests if helpful.