| name | remote-claude-code-tmux |
| description | Run Claude Code (or any long task) persistently on a remote machine using tmux, so it survives SSH disconnects and you can reattach from any device — laptop or phone. Sets up the "tm" one-session-per-folder helper, auto-attach on login, and a Termius/SSH workflow. Use when the user wants Claude Code to keep running on a VPS / server / always-on box after they disconnect, wants to code on a remote machine from their phone, mentions tmux + Claude Code, asks why Claude dies when SSH drops, or wants persistent remote development sessions. |
remote-claude-code-tmux
Run Claude Code on a remote machine so it survives disconnects. Works on any
always-on box — a VPS, a spare desktop, a Raspberry Pi. Disconnect, close your
laptop, reconnect from your phone hours later — Claude is still running, right
where you left it.
This is a self-contained module. It's used by the
levelsio-vps-setup-skill
(Path B — develop on the VPS the levels.io way) and published on its own because
the capability is useful anywhere you have a remote box.
The problem it solves
SSH into a box, start claude, close your laptop — the SSH session dies and
Claude Code dies with it, mid-task. tmux fixes this: it runs your shell (and
Claude) inside a session that lives on the server, independent of any
connection. As levels.io puts it, with Claude in tmux on the box "it just keeps
going all night while you sleep," and you can "switch to phone or any other device
whenever you want to continue."
What is tmux (60 seconds)
tmux = "terminal multiplexer." It hosts long-lived sessions on the machine.
You attach to see one, detach to leave it running. The process inside
(Claude Code, a dev server, a build) never knows you left.
| Action | Keys / command |
|---|
| Detach (leave it running) | Ctrl-b then d |
| List sessions | tmux ls |
| Attach to a session | tmux attach -t <name> |
| New named session | tmux new -s <name> |
| Scroll back / copy mode | Ctrl-b then [ (arrows/PgUp; q to exit) |
| Kill a session | tmux kill-session -t <name> |
Ctrl-b is the "prefix" — press it, release, then press the command key. That's
the only muscle memory you need to start.
Install
On the remote box:
bash scripts/setup.sh
Or by hand: install tmux (apt install tmux), then paste the block below into
~/.bashrc and source ~/.bashrc.
The tm helper — one session per directory
Naming and reusing sessions by hand gets old, and you end up with duplicates.
This helper (the one levels.io uses, written by Claude Code) makes tm
create-or-attach a session named after the current folder. One folder → one
session, forever.
tm() {
command -v tmux >/dev/null 2>&1 || { echo "tmux not installed"; return 1; }
local name="${1:-$(basename "$PWD")}"
name="${name//./-}"
name="${name//:/-}"
if [ -n "$TMUX" ]; then
tmux has-session -t "$name" 2>/dev/null || tmux new-session -d -s "$name" -c "$PWD"
tmux switch-client -t "$name"
else
tmux attach -t "$name" 2>/dev/null || tmux new -s "$name" -c "$PWD"
fi
}
if command -v tmux >/dev/null 2>&1 && [ -z "$TMUX" ] && [[ $- == *i* ]]; then
tm
fi
Why each line earns its place:
tm name override — start a second session in the same folder (e.g.
tm logs next to the default).
./: → - — tmux rejects those characters in session names, so a folder
like sm.example.com becomes session sm-example-com instead of erroring.
- inside-tmux branch (
$TMUX) — switch-client moves you between sessions
without nesting tmux inside tmux (confusing, breaks the prefix).
- auto-attach guard (
[[ $- == *i* ]]) — only fires for interactive logins.
scp, rsync, and ssh server 'somecmd' skip it, so transfers and scripts don't
get trapped in a tmux session.
The everyday loop
ssh you@box
ssh you@box -t "cd /srv/myapp && bash -l"
claude
Connecting from your phone (Termius)
Use Termius (iPhone/Android) or any SSH app. Create a host:
- Address: your box's address (a Tailscale
100.x.y.z IP if you use Tailscale,
so it works from anywhere on cellular).
- User: your SSH user.
- Key: generate one in Termius (Keychain → New Key), copy its public key,
and add it to the box:
echo "ssh-ed25519 AAAA...phone..." >> ~/.ssh/authorized_keys.
- Run on connect:
cd /srv/myapp && tm — lands you in the right session.
Logging Claude Code in on a headless box
No browser on the server, so use the browser-link flow: claude → /login →
Claude prints a URL and a code. Open the URL on any device (your phone's browser
is fine), authenticate, and paste the code back into the terminal. Do this
inside a tmux session so the login persists. (Or set ANTHROPIC_API_KEY.)
Remote-control from the Claude app (optional)
Because Claude lives in a tmux session that never dies, you can also drive it from
the Claude mobile/desktop app's remote session instead of a raw terminal —
handy from a phone. The tmux session keeps the process alive between reconnects.
(Some people do the same with the Codex CLI; tmux is agnostic about what runs
inside it.)
Gotchas
- Started Claude without tmux? It dies on disconnect — that's the whole
point of this module. Always
claude inside a tmux session.
- Don't nest tmux. If you're already attached and run
tmux attach, you get
tmux-in-tmux. The tm helper avoids this via switch-client when $TMUX is set.
- Lost your session list?
tmux ls shows everything still running. If it's
empty, the box rebooted — tmux sessions don't survive reboots (run long-lived
services under systemd, not tmux).
Credit
The tm function and the develop-on-a-remote-box-with-Claude-Code workflow are
inspired by @levelsio (Pieter Levels). Not
affiliated with or endorsed by levels.io.