Feature request: Claude Desktop SSH remote — session should survive client disconnect (reconnect/resume)
When Claude Desktop's SSH remote client disconnects — intentional quit, network drop, laptop lid close — the Claude Code process on the remote server terminates with it. There's no way to reconnect and pick up where you left off.
Verified this today by starting a long-running background bash script (logging a tick every 30s to a file) via the SSH remote session, then disconnecting the client. On reconnect: the bash process was still running and logging fine, but the Claude Code session itself was gone — no way to resume the conversation or see the in-flight tool calls.
For comparison, Codex CLI keeps the session alive server-side; reconnecting reattaches to it, essentially tmux-style. The current workaround for Claude Code is to manually wrap claude inside tmux before connecting, which sidesteps the integrated SSH remote feature entirely.
Expected behavior: the server-side Claude Code session stays alive on client disconnect, and Claude Desktop reattaches to the existing session on reconnect. A --detach/--attach CLI pair would also cover headless cases.
Environment:
- Local client: Claude for Mac 1.3109.0 (35cbf6)
- Remote: Claude Code 2.1.112 on Linux
Related: #49644 (Terminal panel disabled in remote sessions)
17 Comments
+1, this exact scenario is my primary use case. I'm running a remote dev environment on a cloud instance (Ubuntu 24.04) with full toolchain installed (Node, Go, custom CLIs, project files). I want to use Claude Desktop's SSH feature as the primary client because it's the cleanest architecture: UI on my Mac, environment fully on the server.
Setup:
Current behavior is exactly what's described: when I quit Claude Desktop, the remote ccd-cli process terminates and the session is gone. Mobile and web clients (via /remote-control from inside the SSH session) lose access immediately as soon as the desktop app closes, because the desktop app is acting as a relay between the API and the remote process.
The workaround of running claude inside tmux over a manual SSH connection works, but it defeats the entire purpose of the integrated SSH feature — at that point the desktop app is just a worse terminal.
Expected behavior: closing the desktop app should leave the remote ccd-cli process running, and reopening should reattach to it (the way Codex CLI does, as the original report mentions). Bonus: this would also fix the gap where mobile/web access dies when the laptop closes.
Would really appreciate prioritization on this — it's the missing piece that makes the SSH remote feature actually compete with tmux+CLI for serious remote dev work.
+1
+1
+1 Would be nice if the sessions started through ssh also showed up in
claude agentsin the remote ...Same here. Windows 11, Claude Code Desktop with SSH host = Mac mini (macOS 26.5, Apple Silicon). Session works fine with Windows Desktop open, but closing Windows kills the session on the Mac side — iPhone still shows it as "Connected" but messages time out or render extremely slowly. Sessions started directly via Mac Claude Code Desktop with /rc work perfectly. Suggests the issue is specifically in Windows Desktop's SSH-host session lifecycle.
+1 I moved my agents of my main machine and the idea behind that was to not have my Mac constantly open. Especially while traveling this is becoming cumbersome. In an ideal world I can just pick up from my phone when I close my laptop and vise versa.
+1
Codex desktop does this well, and this is a big limitation of Claude desktop
A server-hosted angle: when the remote host is a server that's always on, persistence needs no new infrastructure. The reason a session would otherwise have to move to Anthropic's cloud to persist is that a personal machine might sleep or shut down — but a server doesn't. So the remote agent is being killed on client disconnect when it could just keep running where it already is.
Concrete ask: a per-remote opt-in — "keep the session alive on disconnect" — that leaves the remote agent running on the server instead of killing it when the client goes away. That's all I'd need: with Remote Control already on, a surviving session shows back up in the session list / web / mobile on its own, so there's nothing extra to build for reattach.
Setup: Desktop (Max) → SSH → always-on Linux server,
ccd-cli 2.1.215.Untested theory, grain of salt: the installed binaries carry some reattach/persistence-named symbols and env flags that might be relevant plumbing — a
reattachRPC (ReattachParams/ReattachResult), plusCLAUDE_CODE_FORCE_SESSION_PERSISTENCE,CLAUDE_BRIDGE_REATTACH_SESSION/_SEQ,CLAUDE_CODE_RESUME_SOURCE_ALIVE,onTransportPersistence*. Haven't tested whether they're wired for this case or only for transient reconnects — just a possible starting point.Happy to test a flagged build.
+1
+1 it's a big miss that this is supported in ChatGPT Desktop and not in Claude Desktop
+1
Until this lands natively, the reliable pattern is a persistence layer on the server side: run the CLI inside a detachable session (tmux, or shpool which is lighter), so the SSH client dropping doesn't SIGHUP the agent. You reattach and the conversation is intact.
Disclosure: I maintain Session Kit (https://github.com/dob323/session-kit), an MIT-licensed layer on top of shpool built for this exact Claude-Code-over-SSH case. Beyond surviving disconnects it adds per-session names, a "needs your reply" state, and identity checks so reattaching to many agents stays sane. Public beta, Linux/macOS.
+1 — this hit me constantly: lid close or a network blip and the remote session is gone.
Full disclosure, I ended up building a workaround and it became a product, so grain of salt — but since everyone in this thread has exactly this problem: I run Claude Code inside a persistent workspace (a Docker box that stays alive on its own), and the phone/desktop Claude apps just attach to it.
The session isn’t tied to any client connection, so closing the laptop doesn’t kill anything — you reopen the app and it’s where you left it. Obviously - you continue to use your existing Claude sub via the official CLI.
There’s a self-host edition if you’d rather keep it on your own hardware: https://github.com/Podbay-Cloud/install
Happy to answer questions here, and hoping Anthropic also just fixes RC persistence.
Another one-command option. Disclosure, I work on Pilot Protocol, which develops shell.online.
shell claudeon the server runs claude in a detached PTY session that survives your SSH client dropping,shell listandshell attachreattach from any later login, and the printed browser link is the pick-up-from-phone path. I tested the detach, survive, and reattach mechanics today on macOS and Linux. The session outlives disconnects but not the task, when claude exits the session ends. MIT, https://github.com/TeoSlayer/shell.onlineOn 2.1.251 the survival half of this works — but the daemon has no eviction policy, so sessions just accumulate.
Setup: Claude Desktop (Windows) → SSH remote → Hetzner VPS, 4 GB RAM, Debian 13, Claude Code 2.1.251.
Sessions do survive client disconnect now. Two things I can verify on my box:
server --servedaemon has been up for 3 days (started Aug 29 11:56, still running Sep 1), across many SSH disconnects;What has no counterpart is any form of memory hygiene. Nothing reaps, unloads, freezes or evicts. Every session I have ever opened is still a live
ccd-clichild of the daemon, and each one keeps its own MCP stdio stack running underneath it. Right now on my box:This is not misuse. A Desktop user talks to several conversations in parallel, forgets some, and never comes back to them — that is the normal shape of the product. But every forgotten tab is a resident process for as long as the daemon lives.
The state is already on disk. The transcript under
~/.claude/projects/**/*.jsonlis the durable session; the resident process is a cache of it, and--resumealready proves the round-trip works. So the right primitive is not an idle timeout that kills — it is unload the cold session and rehydrate it when the user reopens it, transparently. Killing is the crude version of the same idea, and it is the only one available from outside.The honest caveat: eviction is lossless only for a session with no in-flight work. Background bash, pending tool calls, MCP servers holding server-side state — those need to either block eviction or be re-established on rehydrate. Which is an argument for doing this inside the daemon, where that is knowable, rather than from outside where it is not.
Meanwhile, from outside, the only honest liveness signal I found is the mtime of the transcript — the process itself tells you nothing: it sits in
epoll_wait, RSS flat, no exit code. So I run a 30-minute systemd timer that walks theccd-clichildren ofserver --serve, resolves each to its transcript (process start time, plus the id in--resume=), and SIGTERMs those silent for over 3 hours. If it cannot resolve a transcript it keeps the process — never kill blind. Crude, but it reclaimed ~2 GB in 12 hours.Even short of full unload/rehydrate, a way to see and end detached sessions (
claude sessions ls/rm) would help. Today the only way to know what is running on your own machine ispgrep.Happy to share the script.
The tmux/shpool answers here solve the client-disconnect half, and on an always-on server that is the whole problem. On a laptop it is only half, which is why "lid close" keeps coming up in this thread specifically.
A detachable session protects the process from SIGHUP when the SSH client drops. It does nothing if the machine underneath goes to sleep. Close a MacBook lid with no external display and macOS sleeps regardless of what is inside tmux, so the agent stops mid-task and there is nothing left to reattach to.
Defeat sleep with
pmset disablesleepor Amphetamine and you land on a second, quieter failure. With the lid shut and no monitor there is no attached display at all, and macOS tears down the display coordinate space. A terminal-only agent keeps working. Anything that renders does not: ScreenCaptureKit returns black, windows migrate to a phantom arrangement, injected clicks land in a space that no longer exists. Awake and observable turn out to be different properties, and the $8 dummy HDMI plug people buy in multipacks exists purely to paper over the second one.That half is fixable in software. CGVirtualDisplay and friends are private API, but they are the same machinery Sidecar and AirPlay-as-display run on, and DeskPad and BetterDisplay have leaned on them across many macOS releases. Create one before the lid shuts and the machine keeps compositing at full rate into a screen nobody is looking at. Writeup with the actual class dance: https://github.com/harshalgajjar/Reminal/blob/main/docs/closed-lid.md
Disclosure, in the same spirit as the two workaround-turned-product comments above: that came out of reminal, which I build. Its shape is different from the options already posted here, which is the only reason I think it is worth adding. There is no session migration and nothing to reattach, because the session never leaves the machine you own. A browser attaches to the PTY that is already running, and to the GUI windows on that machine as well, so the half of the problem that survives tmux is covered too. The "pick up from my phone when I close my laptop, and vice versa" case described upthread is exactly what it was built for.