Cursor SSH Remote Connection Drops Mid-Edit

Cursor's SSH Remote session disconnects while you are editing, losing the agent context and unsaved buffer state. Diagnose the keepalive, network, and remote extension host.

You are deep in a Composer session on a remote dev box over SSH Remote. Halfway through a refactor, the bottom-right status bar flips red: “Disconnected from <host>”. The agent transcript vanishes, an unsaved buffer turns gray, and reconnecting spawns a new remote extension host that does not see your prior chat. This is almost never Cursor “crashing” — it is the SSH transport dying. The fix lives in three layers: client keepalives, network path stability, and the remote extension host’s own lifecycle. Address them in that order and the drops stop.

Common causes

Ordered by likelihood for SSH Remote disconnects.

1. SSH keepalive interval too long (or zero)

A NAT box, corporate proxy, or cloud load balancer silently drops idle TCP flows after 60-300 seconds. If your ServerAliveInterval is unset or higher than the NAT timeout, the connection looks dead to the middlebox and gets reaped.

How to spot it: Drops happen after a quiet pause (you stepped away, agent was thinking). Editing constantly never drops. ~/.ssh/config has no ServerAliveInterval.

2. Wi-Fi roam or VPN handover

Your laptop hops Wi-Fi access points, your VPN reconnects, or your IP changes when a corporate gateway re-leases. The original TCP socket is dead; Cursor’s resilience layer cannot re-bind.

How to spot it: Drop correlates with walking between rooms, sleep/wake, or tailscale status showing a relay swap. The macOS console log shows en0: link state change near the drop.

3. Remote extension host OOMs

The cursor-server process on the remote box hits its cgroup or container memory cap. The host dies, the SSH socket stays up briefly, then Cursor reports lost connection.

How to spot it: dmesg | tail on the remote shows Out of memory: Killed process ... cursor-server. Or the remote ~/.cursor-server/data/logs/<date>/remoteagent.log ends with FATAL.

4. Disk-full on remote $HOME

Cursor writes telemetry, logs, and language-server caches under ~/.cursor-server. A full disk causes the host to error and exit; the agent looks like it disconnected.

How to spot it: df -h ~ on the remote shows 100% use. ~/.cursor-server/data/logs/ is multi-GB.

5. Remote box went to sleep / autoscaler killed the instance

Spot instances, ephemeral preview environments, and laptops left lid-closed all terminate without notice. The SSH session dies cleanly from the kernel side.

How to spot it: uptime on reconnect shows the box just rebooted. Cloud console shows the instance was replaced or preempted.

6. Server-side ClientAliveInterval mismatch

If the remote sshd_config has ClientAliveInterval 30 and ClientAliveCountMax 3, the server kicks the client after 90s of one-way silence — even if the client thought everything was fine.

How to spot it: Drops are very consistent (every ~90s or 120s of idle). Remote /var/log/auth.log shows Timeout, client not responding.

Before you start

  • Note whether drops are time-correlated (always at ~N minutes) or random — deterministic points to a timeout, random points to network or OOM.
  • Capture the exact text of the status-bar error and the timestamp.
  • Confirm whether vanilla ssh <host> from a terminal also drops under the same idle pattern. If it does, the issue is purely SSH transport, not Cursor.
  • Record your ~/.ssh/config keepalive settings before changing anything.

Information to collect

  • Output of ssh -v <host> for the first 10 lines (proves which config + identity loaded).
  • ~/.ssh/config Host block for the dev box.
  • Remote dmesg | tail -50 immediately after a drop.
  • Remote df -h ~ and du -sh ~/.cursor-server.
  • Tail of ~/.cursor-server/data/logs/<latest>/remoteagent.log on the server.
  • Whether you are on Wi-Fi vs ethernet, and whether a VPN/Zero-Trust agent is in the path.

Step-by-step fix

Ordered cheapest to most invasive.

Step 1: Enable client keepalives in ~/.ssh/config

Add or update the Host block:

Host devbox
  HostName 10.0.0.42
  User you
  ServerAliveInterval 30
  ServerAliveCountMax 6
  TCPKeepAlive yes

ServerAliveInterval 30 sends a keepalive every 30 seconds — well under typical NAT idle timeouts (60-300s). ServerAliveCountMax 6 means six missed replies (3 minutes) before declaring dead, which tolerates brief Wi-Fi blips. Restart Cursor’s remote window after editing — the SSH config is read fresh on each new connection, not live.

Step 2: Move logs/cache off the home volume on the remote

If df -h ~ shows pressure:

mkdir -p /var/tmp/cursor-server
ln -snf /var/tmp/cursor-server ~/.cursor-server-logs

Then in Cursor settings (Remote: Server Data Path) point at the larger volume. Also clean old logs:

find ~/.cursor-server/data/logs -mtime +7 -delete

This eliminates cause #4 entirely and usually shrinks the install by several GB.

Step 3: Raise the remote extension host memory budget

If dmesg shows OOM kills, the cursor-server Node process needs more heap. Edit your remote shell rc:

echo 'export NODE_OPTIONS="--max-old-space-size=4096"' >> ~/.bashrc

Then kill the running server so it picks up the new env:

pkill -f cursor-server

Cursor will spawn a fresh remote host on the next connection. For repos over 100k files, 8192 is more realistic.

Step 4: Add a server-side keepalive that matches the client

On the remote, edit /etc/ssh/sshd_config (or the user-level ~/.ssh/sshd_config.d/ if your sshd is configured for it):

ClientAliveInterval 30
ClientAliveCountMax 6

Reload sshd:

sudo systemctl reload sshd

Symmetric intervals prevent the case where the server impatiently kicks a client that thought it was healthy.

Step 5: Switch to a stable transport if the network is hostile

For flaky Wi-Fi or VPN handovers, mosh or tailscale ssh survives IP changes:

brew install mosh

Cursor itself does not speak mosh, but for the underlying box you can wrap a stable tunnel:

tailscale up
ssh -o ProxyCommand="tailscale nc %h %p" devbox

Or use Cursor’s Tunnels feature instead of raw SSH — it auto-reconnects on IP changes.

Step 6: Treat the remote as ephemeral and persist state explicitly

If your dev box is a spot/preemptible instance, save the things that matter outside the box:

# Push uncommitted work to a wip branch every 5 min via a cron on the remote
*/5 * * * * cd ~/proj && git add -A && git commit -m "wip" --allow-empty && git push origin HEAD:wip-$(hostname)

Combine with Cursor’s Chats: Persist Remote History setting so the agent transcript syncs to your local profile. Now a drop costs you nothing.

Verify

  • Open Cursor on the remote, leave it idle for 10 minutes, return — status bar stays green.
  • Run a deliberately long agent task (large refactor) and watch turn-by-turn — no mid-stream disconnect.
  • After a real network blip (toggle Wi-Fi for 5 seconds), the bottom bar should reconnect within 30s without losing the open editors.

Long-term prevention

  • Ship a team-wide ~/.ssh/config template with ServerAliveInterval 30 baked in.
  • Provision dev boxes with at least 8 GB RAM and a logs/cache volume separate from $HOME.
  • Add a daily cron to prune ~/.cursor-server/data/logs older than 7 days.
  • Prefer Tailscale or Cursor Tunnels over raw SSH for laptop-to-cloud sessions.
  • For spot instances, automate wip branch pushes every few minutes.
  • Monitor dmesg and disk usage in your dev-box bootstrap script; alert before the box dies.

Common pitfalls

  • Setting ServerAliveInterval 300 because “less chatter is better” — most corporate NATs drop idle flows in 120s.
  • Editing ~/.ssh/config while Cursor still has an open remote window. The change does not apply until the next connection.
  • Assuming the agent transcript is saved server-side. By default it lives in the local profile; a remote crash plus a stale local cache loses it.
  • Running pkill cursor-server while editing — the buffer goes gray and any unsaved change vanishes. Save first.
  • Blaming Cursor when ssh devbox from a plain terminal also drops at the same interval.
  • Ignoring the remote ~/.cursor-server/data/logs/ directory growing into the tens of GB.

FAQ

Q: Cursor reconnects but the agent forgot everything. How do I keep the chat?

Enable Chats: Persist Remote History in settings — the transcript syncs to your local profile and survives remote host restarts. See also Cursor chat history lost on restart.

Q: My SSH config changes do not take effect.

Cursor reads ~/.ssh/config only when a new remote window is opened. Close the remote window, then Cmd+Shift+P -> “Reload Window” is not enough. Use “Remote: Close Remote Connection” first.

Q: Should I use a persistent screen/tmux on the remote?

It does not help Cursor — the remote extension host is a separate process from your shell. But a tmux for terminals inside the remote is still valuable as a fallback when the extension host restarts.

Q: Is mosh supported directly?

No, Cursor’s Remote-SSH transport uses standard SSH. Use mosh for a separate terminal session and Tailscale or Cursor Tunnels for the IDE itself.

Tags: #Cursor #Troubleshooting #Debug #AI coding