Two Kinds of Drop – Only One Is a Real Problem
If sessions keep breaking off while you run Claude Code on a remote server, one distinction is worth making – because the solution follows from it. There are actually two completely different kinds of drop that are easily lumped together:
- The control connection drops, the process is still alive. That's the case you can “revive” when you reconnect to the server.
- The
claudeprocess itself dies – e.g. viaSIGHUPon an SSH drop, an OOM kill or a crash. That's the “beyond saving” case.
The core of the problem: if claude runs as a child process of your SSH session, a disconnect takes the process down with it. So the solution is always the same – decouple the process's lifetime from the connection.
Primary Fix: tmux on the Server
Never run Claude Code directly in the bare SSH session, always run it inside tmux (or screen). Then a disconnect is only a detach, not a kill:
# on the server, idempotent (attaches if present, otherwise creates):
tmux new -A -s cc
claude
# detach: Ctrl-b d
# after a drop, just hop back in:
tmux attach -t cc # short: tmux a -t ccThis makes practically the entire “beyond saving” case disappear, because the vast majority of drops are pure connection drops – the process keeps running in tmux, unfazed.
This assumes, of course, that the server stays up. In my case that's a power-efficient mini-PC in the homelab – enough RAM for a few Docker stacks and, well, a persistent tmux session that claude hangs on:
Ad · Affiliate link – if you buy through it, I may earn a commission. It doesn’t change the price for you.
What tmux Actually Does
tmux is a terminal multiplexer. The key is its client-server architecture: on the first invocation a tmux server starts in the background as a daemon. Your terminal is only a client. Every program you start inside tmux runs as a child process of that server – not as a child of your SSH session. If SSH drops, only the client disappears; the server, including claude, keeps running, and on reconnect you re-attach to the still-living server.
The hierarchy is server → sessions → windows → panes: a session is a self-contained workspace (e.g. one per project), a window is like a tab, a pane a subdivision of the window.
tmux new -A -s cc # create OR attach session "cc" if it exists
tmux ls # list all sessions
tmux attach -t cc # attach to session "cc" (short: tmux a -t cc)
tmux kill-session -t cc # terminate sessionThe -A in tmux new -A -s cc means attach-or-create: if “cc” already exists, it attaches instead of erroring out. That makes the command idempotent – the same command works on the first start and on every reconnect. That's exactly why you use it.
From the inside, tmux is controlled via the prefix key Ctrl-b: press the prefix, release, then the command key. The most important ones:
Ctrl-b d– detach (disconnect, session keeps running)Ctrl-b c– new windowCtrl-b n/p– next / previous windowCtrl-b 0–9– jump straight to a window numberCtrl-b %/"– split pane vertically / horizontallyCtrl-b+ arrow key – move between panesCtrl-b z– zoom the active pane (toggle)Ctrl-b [– scroll/copy mode (exit withq)
The scroll mode in particular is handy, because a bare terminal over SSH often has no usable scrollback – tmux gives you scrollable history here.
Making the Transport More Robust: SSH Keepalive
Before we swap out the transport entirely: two knobs in the SSH config reduce the dropping itself. ServerAliveInterval sends a regular sign of life so the connection isn't silently cut by firewalls or NAT timeouts:
# ~/.ssh/config
Host myserver
ServerAliveInterval 30
ServerAliveCountMax 6
TCPKeepAlive yesmosh: The Transport That Doesn't Drop
mosh (mobile shell) replaces SSH as the interactive transport and solves exactly its weaknesses on unstable connections. The connection is initially established over SSH – authentication and starting the mosh-server run via your normal SSH config and keys. After that mosh switches to its own UDP protocol; from then on SSH is no longer involved.
Three properties make the difference:
- Roaming: the connection is tied to a session ID, not the IP/port tuple. A network switch (Wi-Fi → cellular), the laptop sleeping or a new IP – the session simply survives it, whereas SSH would drop hard here.
- Intermittent connection: if the network drops briefly, the
mosh-serverwaits and re-syncs by itself when it comes back, without you doing anything. - Predictive echo: mosh synchronizes the screen state instead of a byte stream and shows your keystrokes locally, speculatively. That makes even a high-latency connection feel responsive.
mosh user@myserver -- tmux new -A -s ccBroken down: mosh user@myserver connects via mosh (SSH handshake in the background), -- ends the mosh options, and tmux new -A -s cc is the command mosh runs on the server. The effect: one command from anywhere – and you land straight in your persistent Claude session.
Requirements: mosh must be installed on the client and the server and needs UDP ports 60000–61000 reachable – so open the firewall or port forwarding in the respective VM/LXC if needed. Over Tailscale it goes through anyway. What mosh can't do: its own scrollback (that's what tmux underneath is for) and port forwarding/X11 – set up a tunnel separately via ssh -L when you need one.
Recovery for Real Crashes: claude --resume
If the process does die hard once (OOM, kernel panic, power loss), nothing is lost anyway: Claude Code continuously logs the session locally. The transcripts live as JSONL under ~/.claude/projects/<project>/<session-id>.jsonl, where <project> is your working directory with special characters replaced. To resume:
claude -c # latest session in the current directory (--continue)
claude --resume # interactive picker
claude --resume <name|id> # target a specific session--continue (short -c) automatically finds the most recent session in the current working directory. Important: start from the same directory the session began in – ID resolution is limited to the project directory and its Git worktrees.
Two habits make reviving more reliable: name your sessions (/rename or claude -n "feature-xy") so you can find them unambiguously in the picker instead of guessing between cryptic IDs. And on resume, use the “continue from summary” option when the session is large – it saves tokens on re-reading.
My Complete Setup
My recommended setup stacks three layers: mosh → tmux → claude. That makes the control connection irrelevant, and for the rare real crash you have the on-disk transcript plus named sessions as a safety net. One command, from anywhere:
mosh user@myserver -- tmux new -A -s cc
# then inside tmux: claudemosh ensures that in everyday use you practically never notice a drop; tmux is the safety net for the cases where everything does break down. The point at which a session was “beyond saving” should disappear with this.
What I Left Out
- tmux-resurrect / tmux-continuum persist the session state to disk and even survive a server reboot. For a running
claudeprocess that helps little, because the process state isn't serializable – that's where the--resumelayer steps in instead. - tmux does not survive a server reboot. Disconnect and logout yes, a restart no – then only the on-disk transcript helps.
- mosh has limits: no scrollback of its own (hence tmux underneath), no port forwarding/X11. Both are bearable in everyday use.
Conclusion
The actual takeaway is simple: as long as claude runs as a child of the SSH session, every disconnect is a gamble. Decouple the process with tmux and make the transport robust with mosh, and “beyond saving” turns into a harmless detach. And for the rest, there's claude --resume.
Ad · Affiliate link – if you buy through it, I may earn a commission. It doesn’t change the price for you.