SSH / VNC Guide April 23, 2026

2026 TCP keepalive, TCP_USER_TIMEOUT, and surviving idle SSH to a rented ProxyMac Mac mini

ProxyMac Engineering Team April 23, 2026 ~12 min read

Operators stretching SSH from Europe or the US east coast into a ProxyMac Mac mini in Hong Kong, Tokyo, Seoul, Singapore, or the United States routinely see sessions that survive active typing yet vanish after 7–25 minutes of idle Cursor or tail -f. The failure is rarely “SSH is broken on Apple Silicon”—it is almost always a silent middlebox timer combined with default sockets that stop proving liveness. This article gives you a two-layer mental model (OpenSSH keepalives plus kernel TCP behavior), a decision matrix keyed to observable symptoms, a copy-paste ~/.ssh/config stanza with numeric defaults (30-second application probes, four misses before client abort), a seven-step evidence runbook that cites concrete counters (120-second carrier idle timers in many guest Wi-Fi stacks), and links to companion guides on autossh, Mosh, and keepalive, long-RTT client tuning, and MTU black holes so you do not chase TCP when IPQoS is the culprit.

Bookmark help center bootstraps and compare regions on pricing once the socket path is proven stable—otherwise every new hire repeats the same packet-loss folklore.

When idle SSH “dies quietly” on long paths

Symptoms cluster into three buckets: (A) the terminal freezes with no banner and no Broken pipe until you press a key; (B) the client prints client_loop: send disconnect: Broken pipe exactly 120–900 seconds after the last packet; (C) interactive shells stay up but scp or rsync stalls at 0% indefinitely. Bucket A usually means an upstream NAT binding expired without sending RST—common on hotel Wi-Fi whose UDP voice prioritization starves idle TCP. Bucket B often maps to stateful firewalls that enforce a maximum silent flow duration. Bucket C overlaps with PMTUD black holes; cross-check with the MTU article before you spend a weekend tweaking keepalive math.

  • Screen Sharing over SSH tunnel amplifies idle gaps because video frames pause while keystrokes do not.
  • Jump hosts multiply timers—each hop may enforce its own 300s idle policy.
  • LaunchAgent-driven git pulls look idle to carriers even though work happens server-side.

Kernel TCP keepalive vs OpenSSH ServerAliveInterval

The kernel’s TCP stack can emit keepalive segments after net.inet.tcp.keepidle-driven delays on macOS, but you do not control remote middleboxes from sysctl alone. OpenSSH’s ServerAliveInterval instead injects SSH protocol messages that reset higher-layer timers without waiting for OS defaults that may exceed two hours. Treat them as complementary: SSH keepalive proves the SSH connection is alive; TCP keepalive proves the four-tuple is still honored end-to-end.

Numbers to cite in tickets: start with 30s application probes, 4 consecutive misses before disconnect, and document that your mini’s path RTT is under 280ms on average so finance knows you are not masking a region mis-pick.

Tuning matrix keyed to failure class

Symptom signatureFirst knobSecond knobEscalation
Idle > 15m, instant recovery on keystrokeServerAliveInterval 30Reduce jump countCapture Wi-Fi BSSID and compare against guest Wi-Fi guide
Broken pipe at multiples of 300sTCPKeepAlive yes client-sideEnable ControlPersist sockets per ControlMaster guideAsk ISP for documented idle timer
SCP stalls mid-file with no idle gapCheck MSS clampRun PMTUD testsSwitch region per latency matrix
Only overnight builds failNightly curl cron from miniLog WAN DHCP renewalsMove job to mini-originated pull

Client ~/.ssh/config recipe for ProxyMac stanzas

Name each region explicitly so new teammates inherit the same numbers. The stanza below targets a HK mini but copies cleanly to JP/KR/SG/US host aliases. Adjust IPQoS only after you read the long-RTT guide—mixing throughput and lowdelay classes incorrectly can starve keepalive packets during bulk copy.

Host proxymac-hk-dev HostName your-node.proxymac.com User youruser ServerAliveInterval 30 ServerAliveCountMax 4 TCPKeepAlive yes IPQoS throughput ControlMaster auto ControlPath ~/.ssh/cm-%r@%h:%p ControlPersist 10m

Seven-step probe runbook your SRE can paste into Confluence

  1. Record baseline RTT with ping -c 20; store median and p95.
  2. Open plain SSH, idle 12 minutes, note whether shell returns.
  3. Enable verbose ssh -vvv on a disposable session; grep for expecting SSH2_MSG_KEXINIT stalls.
  4. Parallel SCP of a 512MB file; if throughput collapses after 90s, suspect MTU not keepalive.
  5. Log VPN state if split-tunnel corporate VPN is on—many enforce idle UDP differently from TCP to the mini.
  6. Server-side ClientAliveInterval (if you control sshd) mirrors client policy; coordinate so server does not send 10s probes while client sends 30s—wasted energy.
  7. Archive final config beside your OpenClaw gateway systemd/launchd notes so automation and humans share one truth.

Pair with autossh, Mosh, and long-RTT tuning—without double-counting fixes

The autossh and Mosh guide already covers process supervision and UDP-based resilience. Add this TCP article when plain OpenSSH over TCP must remain in the loop—for example git+ssh remotes, ssh -L port forwards to local OpenClaw gateways, or rsync --partial over SSH where Mosh cannot substitute. If Cipher or compression changes are on the table, sequence them after keepalive proof because AEAD ciphers interact with CPU load on M4 during large copies, as described in the IPQoS and cipher tuning article.

Document the exact wall-clock when you layered each change. Teams that flip ServerAliveInterval, enable ControlPersist, and install autossh on the same afternoon rarely know which lever actually fixed overnight CI—your future self needs a dated changelog entry per hop. When multiple engineers share one bastion account, also log source UDP port entropy on Wi-Fi clients; some operating systems randomize ephemeral ports aggressively and confuse stateful firewalls that key only on the server’s :22 tuple.

Finally, correlate silent drops with MTR loss patterns: if loss spikes only during idle periods, you are almost certainly fighting middlebox timers, not congestion. If loss appears under load, revisit PMTUD before you spend political capital asking networking to “raise the keepalive.”

Warning: aggressive ServerAliveInterval 5 storms some DDoS appliances—raise gently if enterprise edge devices send TCP RST after probe bursts.

FAQ

Should I lower sysctl tcp keepidle on the mini? Usually no for customer-managed laptops; fix the client first. Data-center adjacent minis rarely initiate the flaky leg.

Does ControlMaster make idle timers worse? It can multiplex many sessions onto one TCP flow—good for handshake cost, bad if that single flow hits a 60s UDP-starved Wi-Fi timer; watch per-path counts.

What about IPv6-only uplinks? Happy eyeballs can mask IPv4 blackholes; document which address family your SSH target uses before you tune timers differently per stack.

Why a dedicated ProxyMac Mac mini makes TCP hygiene measurable

Apple Silicon M4 on a leased mini gives you a stationary endpoint in HK / JP / KR / SG / US with predictable peering—contrast that with a developer laptop hopping between café NATs. You can run continuous sysctl and nettop baselines, attach OpenClaw automation to the same host that owns your CI secrets, and still open Screen Sharing when GUI trust prompts appear. Pair this network discipline with transparent per-region pricing so finance sees why a second mini for APAC-only timers beats overloading a single tunnel that collapses every Friday night.

Rent the region that matches your timers

HK / JP / KR / SG / US Mac mini with SSH ready