What Breaks When You Run Claude Code Over SSH — And Three Fixes
SSH into your remote server, start Claude Code, and half the UI is gone — diff viewer, task panels, changed-files sidebar, all of it. Here's why the terminal UI collapses over SSH and how Leo, Claudette, and tmux each solve it differently.
Running Claude Code over SSH gets you persistence and remote access, but it collapses the terminal UI: the file diff viewer, task planning panels, and changed-files sidebar all stop rendering, leaving only a bare chat interface. The root cause is a terminal capability mismatch between Claude Code's TUI renderer and SSH's emulation chain. Three community tools — tmux with correct $TERM configuration, Leo's HTTP web dashboard, and Claudette's encrypted WebSocket workspaces — each solve this problem differently. This post walks through what breaks, why, and how to fix it.
TL;DR
The SSH + Claude Code combination degrades to chat-only. The rich terminal UI — /diff, task panels, file tree — requires a proper TTY with matching terminal capabilities. SSH frequently breaks this chain. Your agent keeps running; you lose the interface context you need to steer it. Three community fixes exist: tmux (persistence, partial UI fix), Leo (replaces SSH with an HTTP dashboard), and Claudette (purpose-built remote workspaces over encrypted WebSocket). If you want to eliminate the problem category entirely rather than patch around it, an always-on cloud VM with a purpose-built interface is the next step.
What Actually Breaks — And What Doesn't
A developer on r/ClaudeCode summed it up directly:
"Is it really just a chat interface and slash commands? No ability to open views such as changed files, planned tasks, etc? I want to use it because this allows me to run Claude over ssh for remote sessions or even sessions under a restricted user on my Mac machine."
That's an accurate description of the degradation. The exact breakdown:
| Feature | Over SSH | Notes |
|---|---|---|
| Chat interface | ✅ Works | Pure text I/O survives |
| Slash commands | ✅ Works | Text-based, no TUI dependency |
File diff viewer (/diff) |
❌ Breaks | Requires TUI renderer |
| Changed-files sidebar | ❌ Breaks | Requires TUI renderer |
| Task / plan panels | ❌ Breaks | Requires TUI renderer |
| Agent persistence | ⚠️ Partial | Exits on disconnect without tmux |
| Session resumption | ⚠️ Partial | .jsonl transcript survives; UI state doesn't |
The pattern: anything that depends on Claude Code's TUI (terminal user interface) renderer breaks. Anything that's pure text survives.
Why Does the Terminal UI Collapse Over SSH?
Claude Code's rich UI components use terminal control sequences — ANSI escape codes — to draw and update the display in place. These sequences depend on three things being correct simultaneously:
- A
$TERMvalue that matches your actual terminal capabilities — Claude Code's TUI emits sequences appropriate for your declared terminal type. If that type doesn't match, the sequences either don't render or print as garbage. - Correct terminal dimensions — the UI needs accurate
$COLUMNSand$LINESvalues, or must receiveSIGWINCHwhen the window resizes. SSH + tmux chains frequently get this wrong. - A fully allocated pseudo-TTY on the remote machine — without a PTY, the TUI initialization path fails silently and falls back to plain text.
Over SSH, several things can go wrong with all three:
- TERM mismatch: Your local terminal is
xterm-256color, but the remote shell hasTERM=vt100or unset. The sequences Claude Code emits are meaningless to the declared terminal type. - No PTY allocated: If you're running Claude Code via a piped or scripted SSH command without
-t, there's no pseudo-TTY at all. The TUI can't initialize. - tmux/screen TERM shadowing: Running inside tmux without setting
TERM=screen-256color(ortmux-256color) causes color and cursor-positioning sequences to misfire.
This isn't a Claude Code bug in isolation — it's the structural mismatch between a TUI designed for a local terminal session and the emulation chain SSH introduces. There's an active bug report on GitHub documenting exactly this pattern: VSCode Remote SSH users hitting extremely slow tool execution and an unresponsive UI after Claude Code 2.1.20+.
Fix 1: tmux with Proper TERM Configuration
tmux is the baseline fix. It solves persistence — your session survives SSH disconnects and laptop sleeps — and with correct configuration, improves (but doesn't fully solve) the UI rendering problem.
Configure tmux for 256-color support:
# ~/.tmux.conf on the remote machine
set -g default-terminal "screen-256color"
set-option -ga terminal-overrides ",xterm-256color:Tc"
Start a named session and launch Claude Code inside it:
# On the remote machine
tmux new-session -s claude-main
export TERM=screen-256color
claude
Detach and reattach across SSH connections:
# Detach (session keeps running):
Ctrl-b, d
# Reconnect from any SSH session:
tmux attach -t claude-main
Verify the session survived a disconnect:
# From a new SSH connection:
tmux list-sessions
# Expected output:
# claude-main: 1 windows (created Wed May 6 14:23:11 2026) [220x50]
The remaining limitation: Even with tmux correctly configured, the diff viewer and task panels often don't restore correctly when you reattach over a new SSH connection. The agent is alive and responsive. The UI context is gone. You're back to chat plus slash commands. tmux solves the persistence problem; it doesn't solve the TUI rendering problem.
For a complete tmux workflow — named sessions per project, multiple parallel agents in separate windows, and session recovery — the tmux + Claude Code guide covers the full setup.
One critical note: Claude Code exits when SSH disconnects because the OS sends SIGHUP to all foreground processes in the session. tmux intercepts this — the session lives inside the tmux server process, not your SSH connection. But Claude Code must already be running inside tmux before you disconnect. If it's running directly in your SSH shell, a disconnect kills it.
Fix 2: Leo — HTTP API + Web Dashboard
Leo takes a different architectural approach: instead of improving the SSH + TUI experience, it replaces it. Leo exposes Claude Code sessions via a token-authenticated HTTP API and serves a web dashboard — you access your agent through a browser, not a terminal.
This sidesteps the $TERM mismatch problem completely. The browser renders the UI; the SSH tunnel becomes optional or can be replaced by any network path you control.
What Leo adds:
- 24/7 persistence via a tmux daemon with auto-restart — sessions survive crashes, not just disconnects
- Token-authenticated HTTP API — access isn't a raw open port
/agentspawning — create new Claude Code agent instances from the dashboard/tasksmanagement — view and coordinate tasks across multiple sessions/compactper channel — context compaction without touching the CLI- Remote-control flag integration — Leo wires into the
--remote-controlflag in the Claude CLI, which causes agents to appear in the Claude mobile app
The tradeoff: you're running a daemon layer you own and maintain. Leo is a community tool, not an Anthropic product. If Leo's daemon exits between auto-restarts, your sessions are unavailable until it recovers.
Fix 3: Claudette — Encrypted WebSocket Remote Workspaces
Claudette takes the most complete architectural approach. Its remote workspaces feature is designed specifically to replace SSH tunneling as the transport layer, not patch around it.
The key design decisions:
- Encrypted WebSocket transport — works through firewalls and NAT without port forwarding or SSH tunnel management
- Parallel agents on git worktrees — each agent gets an isolated workspace with per-workspace metrics
- Sessions that outlive the local client — the workspace runs on the remote machine; your local client connection is incidental
For Mac Mini or VPS setups where you want something closer to a managed remote workspace than a glorified SSH session, Claudette is the most purpose-built of the three options.
The tradeoff: you're adopting Claudette's workspace model. It's more setup than tmux, and you're dependent on Claudette's maintenance cadence for the workspace runtime.
Which Approach Should You Choose?
| tmux (raw SSH) | Leo | Claudette | |
|---|---|---|---|
| Fixes UI rendering | Partial | Yes (browser) | Yes (native) |
| Session persistence | Yes | Yes + auto-restart | Yes |
| Authentication | SSH key only | Token-authed HTTP | Encrypted WebSocket |
| Parallel agent support | Manual | /agent command |
Worktrees + metrics |
| Setup complexity | Low | Medium | Medium–High |
| Maintenance overhead | None | Daemon required | Workspace runtime |
| Works with Claude mobile app | No | Yes (remote-control flag) | Unknown |
For a deeper comparison of community-built control layers including Leo, see Leo, ADHDev, tmux-notify, AIPass: 4 DIY Control Layers Compared.
How to Verify Your Setup Actually Works
Before trusting any of these configurations with a long-running task, run three checks:
1. Disconnect survival test:
# Inside your tmux or remote workspace session:
sleep 300 &
echo "background PID: $!"
# Kill your SSH connection or close the terminal
# Wait 30 seconds
# Reconnect and verify:
jobs # should show "sleep 300 &"
# or:
ps aux | grep sleep
2. UI component test:
Make a file change, then trigger the diff viewer:
echo "test" >> README.md
Then in Claude Code: type /diff. If the diff viewer renders a formatted output showing the change, your terminal chain is working. If you see raw escape codes or nothing, your $TERM configuration needs adjustment.
3. Session resumption test:
Note your session ID (visible in Claude Code's header or in ~/.claude/projects/<encoded-path>/). Kill your terminal entirely. Reconnect and verify the agent resumes from the same context rather than starting fresh.
What Anthropic Is Building (And What It Doesn't Cover Yet)
The official Claude Code Remote Control docs describe a feature that lets you continue local sessions from any device. But as Codebridge's technical analysis notes, execution stays local: Remote Control is a remote interface to a session that still runs on your original machine, with the terminal still open. That means the SSH UI degradation problem persists for the underlying session — Remote Control moves the control plane, not the execution.
Security researchers at Penligent also document a hard architectural limit: one remote session at a time, and the terminal must stay open. For true always-on operation on a dedicated machine, Remote Control isn't sufficient on its own.
How Grass Makes This Workflow Better
The three approaches above patch the SSH + terminal UI problem. Grass eliminates the problem category.
The root issue with SSH remote Claude Code: you're forced to care about terminal emulation chains, $TERM variables, tmux daemon maintenance, and port configuration. These are infrastructure concerns that have nothing to do with your actual agent work.
Grass is a machine built for AI coding agents — an always-on cloud VM where Claude Code, Codex, and OpenCode run as first-class residents. You don't SSH into it with a terminal. You connect from your phone's native app, your laptop via MCP dispatch, or an automation. The interface is purpose-built for agent interaction: chat, diff viewer, permission approval modals, and session history — all rendered correctly, every time, because there's no terminal emulation chain to misconfigure.
Concretely, what changes:
- No
$TERMmismatch — the mobile app renders its own native UI, not a terminal emulator over SSH - No tmux daemon to maintain — the VM stays running; sessions survive without a process manager
- No open SSH port to expose — connection goes through the Grass network layer
- Permission requests forward to your phone — approve or deny bash commands and file writes from a native modal, wherever you are
- BYOK (bring your own key) — your Anthropic API key stays yours; Grass never touches it
Getting started:
- Go to codeongrass.com — free tier includes 10 hours, no credit card required
- Your VM comes pre-loaded with Claude Code, Codex, and OpenCode — no setup or configuration
- Connect from the mobile app — diff viewer, file browser, and permission modals work out of the box
If you prefer to keep agents running locally, npm install -g @grass-ai/ide and grass start in your project directory gives you the same mobile-native interface over local WiFi — same UI, no SSH, sessions survive disconnects via SSE replay with Last-Event-ID support.
The self-check: if you removed the Grass section, all three fixes above still work end-to-end. Grass is the next level, not the prerequisite.
FAQ
Why does Claude Code's diff viewer not work over SSH?
The diff viewer is part of Claude Code's TUI, which uses ANSI escape sequences to draw in-place display updates. Over SSH, if $TERM on the remote machine doesn't match your actual terminal's capabilities — or if you're inside tmux with a mismatched TERM setting — these sequences don't render correctly. The fix is either configuring TERM correctly across the entire chain, or using a tool that bypasses the terminal renderer entirely (Leo's browser-based dashboard, Claudette's WebSocket workspaces, or Grass's always-on cloud VM with a native mobile interface).
Can I run Claude Code on a Mac Mini and access it remotely? Yes. The most reliable setup is: tmux for persistence, plus Leo or Claudette to replace the SSH + terminal UI with something that renders correctly. Raw SSH with tmux keeps the agent alive but degrades the UI to chat-only. If you want the full interface without terminal configuration overhead, an always-on cloud VM is the cleaner path — Grass provides one pre-configured with Claude Code, Codex, and OpenCode.
What is the difference between tmux and Leo for Claude Code remote access?
tmux is a terminal multiplexer — it keeps your shell session alive after disconnection, but you still access it via SSH and a terminal emulator. Leo replaces that access model entirely: it exposes Claude Code sessions through a token-authenticated HTTP API and a web dashboard. The result with Leo is better UI rendering (browser renders it, not a terminal), no $TERM configuration needed, and additional management primitives (/agent, /tasks, /compact). The tradeoff is a daemon layer you maintain.
Does Anthropic's Remote Control feature solve the SSH UI problem? Partially. Remote Control lets you continue a local Claude Code session from another device, but execution still happens on the original machine and the terminal must stay open. It doesn't move execution to a remote server, and it doesn't fix the terminal UI degradation for users running SSH into a headless machine. For true always-on remote operation where your laptop can be closed, you need either a cloud VM or a persistent remote workspace runtime.
Why does Claude Code exit when I disconnect from SSH?
When your SSH connection drops, the OS sends SIGHUP to all foreground processes in the session. Claude Code receives the signal and exits. The fix is running Claude Code inside a tmux session that was started before you disconnect — tmux's server process owns the session, not your SSH connection, so the signal doesn't reach Claude Code. See the full explanation and recovery steps here.
Running Claude Code on a remote machine seriously enough to hit these problems means you've outgrown the local laptop setup. tmux plus Leo or Claudette solves it — but it's infrastructure you own and debug. An always-on cloud VM where the UI just works, accessible from your phone or any surface, is the upgrade when the maintenance overhead stops being worth it.
Try Grass — 10 hours free, no credit card required.
This post is published by Grass — a machine built for AI coding agents that runs Claude Code, Codex, and OpenCode on an always-on cloud VM, accessible from your phone, laptop, or automation. Agent-agnostic by design.