code · · 7 min read
by Colin Domoney
I’m writing this from South Africa, working remotely against my infrastructure back in the UK. On paper the connectivity is fine: 4G/5G cellular with 35 to 40 Mbps down and a perfectly respectable 7 to 10 Mbps up. Browsing is fine. Downloads are fine. Interactive terminal work is misery. SSH sessions freeze for long stretches, the client disconnects at random, and typing feels like wading through treacle.
The bit that actually hurt: when an SSH connection drops mid-session in Claude Code, the agent context goes with it. What should be a network annoyance becomes twenty minutes of reconstructing where the agent was and what it had done. That was the point at which I stopped tolerating the problem and started fixing it.
One thing no tool can fix: the geographic round trip from South Africa to the UK is roughly 250ms, and cellular adds its own wobble on top. That’s physics. The goal is not to eliminate the latency but to survive the drops and hide the delay.
The obvious knobs are the ones everybody reaches for first, and none of them help here. Compression (ssh -C) trades CPU for bandwidth, and bandwidth was never the problem; terminal payloads are tiny. Keep-alives (ServerAliveInterval and friends) mask symptoms at best and never reliably worked for me anyway. autossh just restarts a TCP connection after it has already died, which is exactly the moment your context has already gone.
The real culprit was the transport. My SSH ran through Cloudflare Tunnels, which wrap SSH in a WebSocket over TCP through Cloudflare’s edge. On a clean office line you would never notice. On a lossy cellular link it is close to worst-case: an extra hop, two layers of TCP fighting each other to retransmit, and head-of-line blocking where a single lost packet stalls everything behind it. When I swapped the transport out, even plain SSH felt dramatically better before I had changed anything else.
Three layers, each solving one distinct problem.
Tailscale replaces the tunnel. It builds a WireGuard mesh over UDP with a direct peer-to-peer path between my laptop and each server, and UDP degrades gracefully on a lossy link in a way tunnelled TCP simply does not. The side benefit is that every client just works: no ProxyCommand incantations, just a plain hostname that tools like scp, rsync and GUI file managers all understand.
mosh replaces the SSH session itself. It is not a reconnect wrapper; it is a different protocol that runs over UDP and synchronises screen state rather than a byte stream. The session survives IP changes, tower roaming, sleep and wake, and multi-minute outages. Better still, it does local echo: keystrokes render instantly on my side instead of waiting for the round trip. This is the layer that hides the 250ms, and it is the difference between typing and treacle.
tmux is the persistence layer. The session lives on the server, so a dead connection costs nothing. Reattach and Claude Code is still there, still streaming output, entirely unaware that anything happened.
The daily driver is one command:
mosh you@yourserver -- tmux new -A -s main
That connects, then creates a session called main or reattaches to it if one already exists.
South African mobile networks put you behind CGNAT, which can prevent a direct WireGuard path and force traffic through one of Tailscale’s relays. I expected this to bite. It didn’t:
tailscale ping tou-main-server
# pong via 187.77.177.5:41641 in 250ms
A via <ip>:<port> response means a direct connection; a relayed one would say via DERP(...). Tailscale only needs one side of the pair to be traversable, and my UK boxes are not behind CGNAT. Even if it had fallen back to a relay, everything would still work with the same hostnames and commands, just with lower bulk throughput.
My acceptance test was toggling airplane mode for thirty seconds mid-session. mosh shows a quiet “last contact” banner, then resumes the instant the radio comes back. The tmux session underneath is untouched. Claude Code never noticed. Compare that with the old setup, where the same thirty seconds meant a dead terminal and a lost agent.
None of these tools is new, and none of them is clever on its own. The trick is that each one refuses to solve the others’ problems: Tailscale only moves packets, mosh only keeps a session alive and hides the delay, tmux only remembers what you were doing. Stack them and a hostile network becomes boring.
The latency is still there. I just can’t feel it.