Workflows: SSH, Nesting, Editor Integration, Pair Programming

SSH and Reconnection

The classic tmux use case: you're on a remote box, the connection drops, you want to pick up where you left off.

On the remote:

ssh you@server
tmux new -A -s work            # attach to or create session "work"
# ... do long-running work

Connection drops. No panic. Reconnect:

ssh you@server
tmux a -t work

Everything's still there. Running processes still running. Scrollback intact.

Auto-attach on SSH

Put this in your remote shell's rc file (~/.bashrc or ~/.zshrc):

if [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ]; then
  tmux new -A -s main
fi

Every SSH login drops you straight into a tmux session named "main". Some people love this; some find it annoying when they want a quick throwaway shell. If you want an escape hatch:

if [ -n "$SSH_CONNECTION" ] && [ -z "$TMUX" ] && [ "$1" != "plain" ]; then
  tmux new -A -s main
fi

Nested Sessions: The Prefix Problem

Run tmux inside tmux (common when SSHing from a tmux pane on your laptop to a tmux session on a server). Both sessions listen for the same prefix. Your prefix press goes to the outer session, never reaching the inner one.

Three ways to cope:

1. Press prefix twice

<prefix> <prefix> key

The first prefix gets consumed by the outer; the second (sent via send-prefix) reaches the inner. This works but is clumsy.

2. Use a different prefix on remote

On the remote server's .tmux.conf:

set -g prefix C-s
bind C-s send-prefix

Now your laptop session uses Ctrl-a and the remote session uses Ctrl-s. No conflict.

3. Toggle outer-prefix mode

A popular pattern: bind a key on your laptop to "pass prefix through for a while":

bind -T root F12 set prefix None \; set key-table off \; if -F '#{pane_in_mode}' 'send-keys -X cancel'
bind -T off F12 set -u prefix \; set -u key-table \; refresh-client -S

Press F12 to disable your laptop's prefix entirely, so every key passes to the remote. Press F12 again to re-enable. Not for everyone, but if you spend a lot of time nested, worth learning.

Editor Integration

vim and tmux side by side

Open vim in one pane, a shell in another. Use <prefix> o or vim-style pane nav (if configured) to switch. Run tests in the shell pane, see results, back to editor.

vim-tmux-navigator

The seamless version. With the plugin in both vim and tmux:

Ctrl-h       move one pane left (works whether you're in vim or a shell)
Ctrl-j       down
Ctrl-k       up
Ctrl-l       right

No prefix. The same key navigates between vim splits or between tmux panes as appropriate. Once you have this, you stop thinking about the split boundary at all.

Sending commands from vim to a tmux pane

The vim-slime plugin (jpalardy/vim-slime) sends text from vim to a REPL in another tmux pane. Great for iterating on SQL, Python, or Elixir:

# in vim, select a region and press C-c C-c

Text goes to whichever pane you configured. Works with Jupyter, iex, psql, whatever.

Pair Programming Over a Shared Session

Two users on the same machine, sharing a tmux session.

Same user, two clients

Trivial. Both people attach:

tmux a -t pair

Both see the same panes. Both cursors move; whichever keyboard types wins.

Different users, same machine

Create a shared socket:

tmux -S /tmp/pair new -s pair
chmod 777 /tmp/pair
chgrp developers /tmp/pair      # optional: group-restrict

The other user attaches:

tmux -S /tmp/pair a -t pair

Over SSH

Both people SSH to the same box, one creates the session, the other attaches. Free, works everywhere, no extra tools.

For more polished pairing, tmate forks tmux and adds peer-to-peer sharing with tokenised URLs. Same keybindings as tmux; takes a few minutes to set up.

Session Templates per Project

Chapter 9 covered scripted sessions. The workflow:

~/bin/start-api                   # one command starts the session

The script either creates the layout or attaches to the existing session. Over time you accumulate one script per project. A shared bin/ directory in your dotfiles keeps them versioned.

For declarative templates, tmuxp / tmuxinator (chapter 9) let you describe a session in YAML. Either is a good step up from shell scripts if your templates get long.

Multiple Monitors

You have two external monitors, each showing a different terminal window, each attached to the same tmux session. Shared cursor, shared scrolling. Not always useful, but occasionally exactly right:

# terminal 1
tmux a -t work

# terminal 2 (different window or monitor)
tmux a -t work

Both clients follow the same active window. When you want independent views, use two sessions instead.

Different windows on different monitors

Group sessions let you share windows but keep cursors independent:

tmux new -s work
tmux new -s work-b -t work       # linked; separate active window

Now work and work-b share the pool of windows. Switch work to window 0 on monitor 1, work-b to window 2 on monitor 2, and you see both at once.

Niche. Works well when it fits.

Persistent Background Tasks

Long-running commands that aren't quite daemons:

tmux new -d -s watch 'tail -F /var/log/app.log | grep ERROR'

Starts a detached tmux session running tail | grep. Check in any time with tmux a -t watch. Kill with tmux kill-session -t watch.

Useful for ad-hoc monitoring that you don't want to run under systemd.

Incident Response

Your service is on fire. You need to watch logs, SSH to three servers, check metrics, and take notes, all at once.

tmux new -s incident

<prefix> c -n notes               # notes window
<prefix> c -n logs                # logs
<prefix> c -n servers             # servers
<prefix> , servers                # focus servers window
<prefix> "                         # split
<prefix> "                         # split again
# one pane per server
<prefix> :setw synchronize-panes on    # broadcast commands to all three
ssh prod-1                        # sends to each pane; connects to prod-1, prod-2, prod-3

Sync-panes lets you run tail -f /var/log/app.log on all three servers with one keystroke. Turn it off when you want to interact with one server individually.

After the incident, detach. The session keeps running. Come back tomorrow to review scrollback and write the postmortem.

Quick Dev Environment in a Script

Typical start-dev for a web project:

#!/usr/bin/env bash
set -euo pipefail
cd "$HOME/code/app"

S=app
if tmux has-session -t "$S" 2>/dev/null; then
  exec tmux a -t "$S"
fi

tmux new -d -s "$S"
tmux rename-window -t "$S:1" editor
tmux send-keys -t "$S:editor" "vim ." Enter

tmux new-window -t "$S" -n server
tmux send-keys -t "$S:server" "npm run dev" Enter

tmux new-window -t "$S" -n shell

tmux select-window -t "$S:editor"
exec tmux a -t "$S"

Put in $PATH. One command, full environment. Over time this script grows with your project.

When tmux Meets VS Code and Friends

A common objection: "I use VS Code; why do I need tmux?"

You probably don't, for the editor. Splits, file navigation, and terminal tabs are all in VS Code. tmux still earns its keep for:

  • SSH work. VS Code Remote is great until it isn't. tmux works over any shell
  • Server administration. No GUI, no VS Code. Your Linux box has bash and tmux
  • Background sessions. Tasks that live beyond your editor
  • Pair programming across different editors

If your work is 100% inside a GUI editor, you can skip tmux. If any part of it involves SSH or long-lived shells, tmux repays the learning fast.

Common Pitfalls

"Prefix key collides with my shell." Ctrl-a conflicts with bash/emacs beginning-of-line, for instance. Trade-off: rebind tmux, or remember <prefix> a for the literal sequence.

"Nested sessions are driving me mad." Use a different prefix remotely, or adopt the F12 toggle trick.

"I SSHed out and my tmux session on the laptop disappeared." Your terminal likely timed out. The session survives. Open a new terminal, tmux a, back to it.

"I shared the socket with chmod 777 and now I'm nervous about security." Reasonable. Restrict by group or use tmate for explicit sharing. 777 on a shared machine is not ideal.

"I want tmux only for some SSH targets." Instead of auto-starting in .bashrc, define a shell function: dev() { ssh "$1" -t 'tmux new -A -s main' }. Use dev server when you want tmux; plain ssh server for a quick shell.

Next Steps

Continue to 12-best-practices.md for the habits that separate fluent tmux users from survivors.