tmux Setup Guide
MuxPad renders your server's tmux exactly as-is over SSH — 100% raw tmux, so your prefix, key bindings, and status bar all stay. Because of that, the remote server must have tmux installed. This guide walks through installing tmux, tuning a couple of optional ~/.tmux.conf settings, and making your first connection.
1. Requirements
- tmux on the server. The remote host must have tmux installed (3.2 or newer recommended). MuxPad auto-detects tmux on connect; if it is missing, the connection is blocked and installation guidance is shown. There is no local shell emulation.
- iPad or iPhone on iOS/iPadOS 17 or later.
- An SSH-reachable account with password or ed25519/RSA key authentication.
2. Install tmux
Install tmux with your server's package manager, then confirm the version.
# Ubuntu / Debian
sudo apt update && sudo apt install -y tmux
# Fedora
sudo dnf install -y tmux
# RHEL / CentOS
sudo yum install -y tmux
# Arch
sudo pacman -S tmux
# Alpine
sudo apk add tmux
# openSUSE
sudo zypper install -y tmux
# macOS (Homebrew)
brew install tmux
# FreeBSD
pkg install tmux
Verify the install:
tmux -V
2-b. Or let an AI do it — one prompt
Already running Claude Code (or another AI CLI) on your server? Paste this single prompt and it will install tmux, apply the recommended settings, and wire up Claude Code notifications for MuxPad — everything in sections 2–4:
You are setting up a server for MuxPad (an iPad/iPhone tmux terminal client). Do all of the following, idempotently and without breaking my existing setup:
1. Install tmux if it is not installed (use this system's package manager). Verify with `tmux -V` (3.2+ preferred).
2. Ensure these lines exist in ~/.tmux.conf (append only if missing):
set -g mouse on
set -g set-clipboard on
set -g allow-passthrough all
set -g history-limit 50000
set -g allow-rename off
set -g automatic-rename off
If a tmux server is running, apply them with `tmux source-file ~/.tmux.conf`.
3. Create ~/.claude/hooks/muxpad-notify.sh (chmod +x) with exactly:
#!/bin/sh
[ -n "$TMUX_PANE" ] || exit 0
title="${1:-Claude Code}"; body="${2:-Needs your attention}"
printf '\ePtmux;\e\e]777;notify;%s;%s\a\e\\' "$title" "$body" \
> "$(tmux display -p -t "$TMUX_PANE" '#{pane_tty}')"
4. Merge into ~/.claude/settings.json (back it up first; preserve all existing hooks and settings):
- hooks.Notification += run the script with body "Waiting for your input"
- hooks.Stop += run the script with body "Task finished"
5. (Optional — Claude status badge+message: sidebar shows running=prompt prefix, needs_input=what Claude asks, idle=completion phrase. Requires python3) Create ~/.claude/hooks/muxpad-status.sh (chmod +x) with exactly:
#!/bin/sh
[ -n "$TMUX_PANE" ] || exit 0
s="${1:-running}"; m=""
if [ "$s" = auto ]; then
cur=$(tmux show-options -p -t "$TMUX_PANE" -v @claude_status 2>/dev/null)
case "$cur" in running|idle) exit 0 ;; esac
s=running
fi
j(){ python3 -c 'import sys,json;print(" ".join(str(json.load(sys.stdin).get(sys.argv[1],"") or "").split())[:120])' "$1" 2>/dev/null; }
case "$s" in
running) m=$(j prompt) ;;
needs_input)
m=$(j message)
case "$m" in *"waiting for your input"*) exit 0 ;; esac
;;
idle) m="${2:-🎯 Done}" ;;
esac
t=$(tmux display -p -t "$TMUX_PANE" '#{pane_tty}')
if [ "$s" = off ]; then
tmux set-option -p -t "$TMUX_PANE" -u @claude_status 2>/dev/null
tmux set-option -p -t "$TMUX_PANE" -u @claude_message 2>/dev/null
printf '\ePtmux;\e\e]7777;claude;off;%s\a\e\\' "$TMUX_PANE" > "$t"
exit 0
fi
tmux set-option -p -t "$TMUX_PANE" @claude_status "$s" 2>/dev/null
if [ -n "$m" ]; then
b=$(printf %s "$m" | base64 | tr -d '\n')
tmux set-option -p -t "$TMUX_PANE" @claude_message "$b" 2>/dev/null
printf '\ePtmux;\e\e]7777;claude;%s;%s;%s\a\e\\' "$s" "$TMUX_PANE" "$b" > "$t"
else
printf '\ePtmux;\e\e]7777;claude;%s;%s\a\e\\' "$s" "$TMUX_PANE" > "$t"
fi
Then merge these into hooks in ~/.claude/settings.json (status token per event):
- UserPromptSubmit += muxpad-status.sh running
- SessionStart, PreToolUse += muxpad-status.sh auto (conditional — flips only needs_input/fresh start to running; keeps idle)
- Stop += muxpad-status.sh idle (2nd arg = completion phrase, default "🎯 Done". Do NOT map SubagentStop — it would flip the session to idle mid-work)
- Notification += muxpad-status.sh needs_input (matcher "permission_prompt" — permission requests only, so the idle reminder never overrides idle)
- SessionEnd += muxpad-status.sh off
6. Test: run each script once with a test argument, and show me a summary of everything you changed.
3. Recommended ~/.tmux.conf
MuxPad works out of the box with your existing tmux config — it does not change your bindings or prefix. Two optional one-liners unlock extra features:
set -g mouse on # two-finger scroll & pane selection (touch)
set -g set-clipboard on # OSC 52 — let tmux copy to the iOS clipboard
set -g allow-passthrough all # let servers push notifications to your device (OSC 9/777)
set -g history-limit 50000 # larger scrollback for ⌘F copy-mode search
set -g allow-rename off # keep programs from renaming your windows (tabs)
set -g automatic-rename off
set -g mouse on— enables touch gestures on iPad: two-finger scroll, pane selection, and resizing. MuxPad forwards two-finger scroll as a tmux mouse wheel when this is on.set -g set-clipboard on— enables OSC 52 so text yanked in tmux copy-mode lands on your iPad/iPhone clipboard.set -g allow-passthrough all— lets scripts and hooks on the server send titled notifications straight to your device (see section 4).set -g history-limit 50000— keeps a larger scrollback buffer, so ⌘F copy-mode search covers more history.set -g allow-rename off/set -g automatic-rename off— stop programs (like Claude Code) from overwriting window names via OSC or the running command, keeping your MuxPad window list clean.
Reload the config without restarting tmux:
tmux source-file ~/.tmux.conf
Or, from inside a running tmux session, press your prefix (Ctrl-b by default), then type : and run source-file ~/.tmux.conf.
Using vim / nvim (optional)
If you live in vim/nvim in the terminal, these few lines noticeably improve the editing feel.
setw -g mode-keys vi # vi keys in copy-mode
set -sg escape-time 10 # kill Esc delay (snappier mode switching)
set -ga terminal-overrides ",xterm-256color:Tc" # truecolor (24-bit)
set -g focus-events on # nvim autoread & gitsigns
4. Server-pushed notifications (optional)
With set -g allow-passthrough all, any script on the server can send a titled notification to your device using an OSC 777 notify sequence wrapped in tmux passthrough. This is ideal for signalling that a long build or a remote AI run has finished — for example from a Claude Code notification hook:
printf '\ePtmux;\e\e]777;notify;Build finished;All tests passed\a\e\\' \
> "$(tmux display -p '#{pane_tty}')"
Use all rather than on: on only passes sequences from panes that are currently visible, and notifications matter precisely when you are looking somewhere else.
Claude Code hook setup
To get a notification whenever Claude Code needs your attention, save this script on the server (e.g. ~/.claude/hooks/muxpad-notify.sh, chmod +x):
#!/bin/sh
# Sends a titled notification to MuxPad through tmux (OSC 777).
[ -n "$TMUX_PANE" ] || exit 0
title="${1:-Claude Code}"; body="${2:-Needs your attention}"
printf '\ePtmux;\e\e]777;notify;%s;%s\a\e\\' "$title" "$body" \
> "$(tmux display -p -t "$TMUX_PANE" '#{pane_tty}')"
Then register it in ~/.claude/settings.json:
{
"hooks": {
"Notification": [{ "hooks": [{ "type": "command",
"command": "~/.claude/hooks/muxpad-notify.sh 'Claude Code' 'Waiting for your input'" }] }],
"Stop": [{ "hooks": [{ "type": "command",
"command": "~/.claude/hooks/muxpad-notify.sh 'Claude Code' 'Task finished'" }] }]
}
}
MuxPad also raises a notification on the terminal bell, and can detect when output goes idle to tell you a long-running job has likely finished — no server setup required for those two.
Claude status badge (optional)
MuxPad can show Claude Code's status as a sparkle badge on each Mux row in the sidebar — working (blue, pulsing), waiting for input (orange), or idle (gray). If you run Claude in several panes within one Mux, it shows the status of the pane with keyboard focus. With no hook installed or Claude not running, no badge appears (your sidebar stays exactly as it is). Alongside the badge, a one-line status message appears under the host — the prompt you typed while working, what Claude is asking for while waiting, and the completion phrase when idle.
Two ways to install. ① The easiest is to paste the setup prompt in section 2-b above (or "Copy setup prompt" on the app's first screen) — it installs and registers this status hook too. ② To do it by hand, follow the steps below.
Save a status hook script, separate from the notification one (e.g. ~/.claude/hooks/muxpad-status.sh, chmod +x). It raises no notification — it only records status on a dedicated sequence (OSC 7777) and a tmux pane option.
#!/bin/sh
# Emits Claude status + message to MuxPad (dedicated OSC 7777 + tmux pane option — not a notification).
# Message: running=prompt prefix, needs_input=what Claude asks, idle=completion phrase ($2, default "🎯 Done"). Requires python3.
[ -n "$TMUX_PANE" ] || exit 0
s="${1:-running}"; m=""
if [ "$s" = auto ]; then
cur=$(tmux show-options -p -t "$TMUX_PANE" -v @claude_status 2>/dev/null)
case "$cur" in running|idle) exit 0 ;; esac
s=running
fi
j(){ python3 -c 'import sys,json;print(" ".join(str(json.load(sys.stdin).get(sys.argv[1],"") or "").split())[:120])' "$1" 2>/dev/null; }
case "$s" in
running) m=$(j prompt) ;;
needs_input)
m=$(j message)
case "$m" in *"waiting for your input"*) exit 0 ;; esac
;;
idle) m="${2:-🎯 Done}" ;;
esac
t=$(tmux display -p -t "$TMUX_PANE" '#{pane_tty}')
if [ "$s" = off ]; then
tmux set-option -p -t "$TMUX_PANE" -u @claude_status 2>/dev/null
tmux set-option -p -t "$TMUX_PANE" -u @claude_message 2>/dev/null
printf '\ePtmux;\e\e]7777;claude;off;%s\a\e\\' "$TMUX_PANE" > "$t"
exit 0
fi
tmux set-option -p -t "$TMUX_PANE" @claude_status "$s" 2>/dev/null
if [ -n "$m" ]; then
b=$(printf %s "$m" | base64 | tr -d '\n')
tmux set-option -p -t "$TMUX_PANE" @claude_message "$b" 2>/dev/null
printf '\ePtmux;\e\e]7777;claude;%s;%s;%s\a\e\\' "$s" "$TMUX_PANE" "$b" > "$t"
else
printf '\ePtmux;\e\e]7777;claude;%s;%s\a\e\\' "$s" "$TMUX_PANE" > "$t"
fi
Then register it in ~/.claude/settings.json, with a status token per event:
{
"hooks": {
"SessionStart": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/muxpad-status.sh auto" }] }],
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/muxpad-status.sh running" }] }],
"PreToolUse": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/muxpad-status.sh auto" }] }],
"Stop": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/muxpad-status.sh idle" }] }],
"Notification": [{ "matcher": "permission_prompt", "hooks": [{ "type": "command", "command": "~/.claude/hooks/muxpad-status.sh needs_input" }] }],
"SessionEnd": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/muxpad-status.sh off" }] }]
}
}
OSC updates the badge instantly, and the @claude_status pane option restores it within a few seconds after reconnecting or relaunching the app. It works with polling alone (about 5s) even when allow-passthrough is off.
5. Connect from MuxPad
MuxPad models a connection as three linked pieces — a Host, a User, and a Mux:
- Add a Host — the endpoint (address and port).
- Add a User — reusable credentials (a username with a password or key). One User can be shared across many Hosts and Muxes.
- Create a Mux — pick a host and a user, then give it a name. The Mux name is the tmux session name.
- Tap the Mux. MuxPad SSHes in and starts or attaches the tmux session automatically.
On connect, MuxPad auto-detects tmux. If it is not installed, the connection is blocked and setup guidance is shown. Because the Mux name maps to the tmux session name, tapping a Mux whose name matches an existing server-side session reattaches to it — that is how a session survives quitting the app, and how you continue an iPad session on iPhone.
6. Troubleshooting
- "Connection blocked / tmux required." tmux is not installed on the server. Install it using the commands in section 2, then reconnect.
- Copying in copy-mode doesn't reach my clipboard. OSC 52 is off. Add
set -g set-clipboard onto~/.tmux.confand reload withtmux source-file ~/.tmux.conf. - Server notifications don't arrive. Passthrough is off. Add
set -g allow-passthrough all, reload, and check that your notify sequence is wrapped in the tmux passthrough escape shown in section 4. - Ctrl-S seems to freeze the terminal. That is XOFF flow control pausing terminal output, not a crash. Press
Ctrl-Qto resume, or runstty -ixonto disable the shortcut. - Keys are not reaching my program. Check your tmux key bindings and prefix — a custom prefix or a conflicting binding can intercept keys before they reach your program. MuxPad passes keys through raw, so tmux itself is in charge.