cheat sheet

tmux

Run multiple persistent terminal sessions, windows, and panes inside one shell. Covers the session/window/pane mental model, default keybindings, copy-mode, and a minimal tmux.conf.

tmux — Terminal Multiplexer

What it is

tmux (terminal multiplexer) is an open-source program by Nicholas Marriott that lets a single terminal window host many independent shell sessions, organised into windows and split panes, and — crucially — keep them running when the controlling terminal closes. The killer use case is SSH: start work on a remote host, get disconnected, reconnect tomorrow, and resume exactly where you left off. As of 3.5 (Sept 2024) and 3.6 (2025), tmux is still under active development and gaining features like extended-key encoding, mirrored layouts, popup mouse handling, and finer-grained pane/window hooks.

The main alternatives are:

  • screen — the original GNU terminal multiplexer; older, simpler, often preinstalled, but no native panes and a much smaller config vocabulary.
  • zellij — Rust competitor with floating panes, KDL configs, and an always-visible keybinding hint bar; far gentler learning curve, but younger ecosystem and slightly higher resource use.
  • wezterm — a GPU-accelerated terminal emulator that bundles its own multiplexer (wezterm cli), so you get panes/tabs/sessions without a separate process; only useful if you also use wezterm as your terminal.

Pick tmux for stability, scriptability, and the largest plugin ecosystem; pick zellij for the friendliest out-of-the-box experience; pick wezterm if you already run it as your terminal and want one fewer moving part.

Install

tmux is packaged on every Linux distribution and is a single binary with no runtime dependencies. The system package is usually fine; build from source only if you need the latest features. Notable cut-offs: 3.2+ for display-popup, 3.4+ for menu/popup -T titles and the :Tc colour override generally working, 3.5+ for extended-keys mode 2 and mirrored main-* layouts, 3.6+ for pane-scoped hooks and the command-error hook.

bash
# Debian/Ubuntu
sudo apt install tmux

# Fedora/RHEL
sudo dnf install tmux

# Alpine (containers)
apk add tmux

# macOS
brew install tmux

Output: (none — exits 0 on success)

Mental model: sessions, windows, panes

tmux has a strict three-level hierarchy. A session is the top-level container that survives detach/reattach; a window is like a browser tab inside a session; a pane is a split region inside one window. Most commands act on the current level, so knowing where you are matters.

LevelHoldsSurvives disconnect?Typical use
SessionWindowsYes (it's the whole point)One per project or per remote host
WindowPanesYes (lives in its session)One per major task ("editor", "logs", "server")
PaneA single shellYes (lives in its window)Split for side-by-side commands
text
Session "work"
├─ Window 0 "editor"
│   ├─ Pane 0  (vim)
│   └─ Pane 1  (file tree)
├─ Window 1 "server"
│   └─ Pane 0  (npm run dev)
└─ Window 2 "shell"
    └─ Pane 0  (interactive bash)

Output: (none — diagram only)

The prefix key

Every tmux keybinding starts with the prefix, which is Ctrl-b by default (Ctrl-a after the popular set -g prefix C-a rebind). Press the prefix, release it, then press the command key. Throughout this article a binding written as prefix d means "press Ctrl-b, release, then press d".

bash
# Inside any shell — start a new session
tmux

# Press prefix d (Ctrl-b, then d) to detach
# The session keeps running in the background

Output (after detach):

text
[detached (from session 0)]

Sessions

A session is what makes tmux durable: detach with prefix d and the processes inside keep running. Use named sessions (tmux new -s name) so they're easy to find later, and tmux new -As name for idempotent attach-or-create.

bash
# Create a new named session
tmux new -s work

# List sessions
tmux ls
# Output:
# work: 3 windows (created Sat May 24 10:00:00 2026)

# Attach to a named session
tmux attach -t work

# Attach-or-create (idempotent — won't fail if already exists)
tmux new -As work

# Kill a session
tmux kill-session -t work

# Kill everything
tmux kill-server

Output (tmux ls):

text
work: 3 windows (created Sat May 24 10:00:00 2026)
notes: 1 windows (created Sat May 24 09:14:22 2026)

Session keybindings

These all start with the prefix. The prefix s chooser is the fastest way to hop between sessions without leaving tmux.

BindingAction
prefix dDetach from session (leaves it running)
prefix sInteractive session chooser
prefix $Rename current session
prefix ( / prefix )Previous / next session
prefix LSwitch to last session

Windows

A window fills the visible screen and contains one or more panes. The default keybindings mirror a browser's tab semantics: prefix c creates a new window, prefix , renames, prefix n / prefix p cycles.

BindingAction
prefix cCreate a new window
prefix ,Rename current window
prefix &Kill current window (asks for confirmation)
prefix n / prefix pNext / previous window
prefix 0prefix 9Jump to window by index
prefix wInteractive window chooser
prefix .Move window to another index
prefix fFind window by name
bash
# Create a session with two named windows from the start
tmux new -s work -n editor \; \
     new-window -n server \; \
     select-window -t editor

Output: (none — exits 0 on success)

Panes

A pane is a single shell occupying part of a window. Splits are either vertical (prefix %, creates a column boundary — panes side by side) or horizontal (prefix ", creates a row boundary — panes stacked). The mnemonic from the default key cap is that % looks vertical and " looks horizontal.

BindingAction
prefix %Split window vertically (panes side by side)
prefix "Split window horizontally (panes stacked)
prefix ←/→/↑/↓Move to adjacent pane in that direction
prefix oCycle to next pane
prefix zToggle pane zoom (full screen)
prefix xKill current pane
prefix !Break pane out into its own window
prefix qShow pane numbers — press number to select
prefix { / prefix }Swap pane with previous / next
prefix SpaceCycle through preset layouts
prefix Ctrl-←/→/↑/↓Resize pane (one cell)
prefix Alt-←/→/↑/↓Resize pane (five cells)
bash
# From inside a window — quick three-pane layout
# 1. Start in your shell
# 2. prefix %        -> split vertically
# 3. prefix "        -> split right pane horizontally
# Result:
#   ┌────────┬────────┐
#   │        │        │
#   │  vim   │ logs   │
#   │        ├────────┤
#   │        │ server │
#   └────────┴────────┘

Output: (none — interactive)

Idempotent attach-or-create

The single most-used invocation, especially in shell startup files: tmux new-session -As NAME attaches to a session called NAME if one exists, or creates one if not. Aliasing this lets you "enter the work session" with one short command from anywhere.

bash
# Long form
tmux new-session -A -s work

# Compact form (same thing)
tmux new -As work

# Useful alias in ~/.bashrc
alias work='tmux new -As work'
alias notes='tmux new -As notes'

Output: (none — exits 0 on success)

If you SSH into a server and want every connection to drop straight into the same session, append this to the server-side ~/.bashrc: [[ -z "$TMUX" ]] && exec tmux new -As main. The $TMUX check prevents nested tmux when you reconnect.

Copy-mode

Copy-mode is tmux's pager — it freezes the pane, gives you scrollback navigation, and lets you select and yank text into a tmux paste-buffer. Enter with prefix [; leave with q. The motion keys depend on mode-keys: emacs (default) uses arrow/Page/Ctrl-keys; vi uses h/j/k/l/V/y.

text
# Enable vi-style keys (recommended)
# ~/.tmux.conf
setw -g mode-keys vi

Output: (none — config snippet)

vi mode bindingAction
prefix [Enter copy-mode
qLeave copy-mode
h j k lMove cursor left/down/up/right
Ctrl-u / Ctrl-dHalf-page up / down
g / GTop / bottom of scrollback
/ then text + EnterSearch forward
? then text + EnterSearch backward
n / NRepeat search forward / backward
vStart visual selection
VLinewise selection
yYank selection to tmux buffer
prefix ]Paste most-recent buffer
prefix =Interactive buffer chooser

To copy text from tmux into the system clipboard you need xclip (X11), wl-copy (Wayland), or pbcopy (macOS), plus a tmux binding like bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "pbcopy". Without that step, y only fills tmux's internal buffer.

Mouse support

set -g mouse on enables click-to-select-pane, scroll-wheel scrollback, and drag-to-resize pane borders. It does not disable normal terminal text selection — hold Shift while selecting to bypass tmux and let the terminal's selection take over (useful for copying out to the system clipboard).

text
# ~/.tmux.conf
set -g mouse on

Output: (none — config snippet)

Configuration

tmux reads its config file at startup and on every source-file. It checks two locations in order: the XDG path $XDG_CONFIG_HOME/tmux/tmux.conf (which defaults to ~/.config/tmux/tmux.conf when the variable isn't set) first, then the legacy ~/.tmux.conf. Pick one — having both is the most common silent-misconfiguration trap.

bash
# Modern XDG location (preferred for new setups)
mkdir -p ~/.config/tmux
$EDITOR ~/.config/tmux/tmux.conf

# Legacy location (still works; what most older guides assume)
$EDITOR ~/.tmux.conf

# Print the config file tmux actually loaded
tmux show-options -gv default-shell    # any show-options succeeds only after load
tmux info | head -20                   # version, term, socket, config-file

Output (tmux -V):

text
tmux 3.5a

Reload without restarting (after editing): run tmux source-file ~/.config/tmux/tmux.conf from any shell, or — once you've added the bind r line in the minimal config below — press prefix r from inside tmux.

A minimal ~/.tmux.conf

A short config that fixes the most-complained-about defaults: rebind the prefix to Ctrl-a (easier to reach than Ctrl-b), enable vi keys and mouse, grow the scrollback, use 24-bit colour, and remap pane splits to keys that match what they do (| = vertical, - = horizontal).

text
# ~/.tmux.conf

# --- Prefix
unbind C-b
set -g prefix C-a
bind C-a send-prefix

# --- General
set -g mouse on
set -g history-limit 50000
set -g base-index 1                 # windows start at 1
setw -g pane-base-index 1           # panes too
set -g renumber-windows on          # close gaps when windows are killed
set -sg escape-time 10              # snappier Esc in vim

# --- True colour (24-bit)
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",xterm-256color:Tc"

# --- Vim-like copy-mode
setw -g mode-keys vi
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel

# --- Intuitive splits (and open in current path)
unbind '"'
unbind %
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

# --- Vim-like pane navigation
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# --- Reload config
bind r source-file ~/.tmux.conf \; display-message "tmux.conf reloaded"

Output: (none — config file)

After editing, reload without restarting: press prefix r (with the binding above) or run tmux source-file ~/.tmux.conf from any shell.

Status bar customisation

The status bar at the bottom shows session, window list, and right-side widgets. status-left and status-right accept format strings that include variables like #S (session name), #W (window name), #H (hostname), and any shell command via #(cmd).

text
# ~/.tmux.conf — minimal status bar
set -g status-style 'bg=#1e1e2e fg=#cdd6f4'
set -g status-left '#[fg=#89b4fa,bold] #S '
set -g status-right '#[fg=#a6e3a1]#(whoami)@#H #[fg=#f9e2af]%Y-%m-%d %H:%M'
set -g status-interval 5
setw -g window-status-format         ' #I:#W '
setw -g window-status-current-format '#[fg=#1e1e2e,bg=#89b4fa,bold] #I:#W '

Output: (none — config snippet)

Popups and menus

display-popup opens a centred floating window that runs any command and disappears when the command exits — perfect for a transient lazygit, htop, or nnn launcher without losing your underlying layout. display-menu builds a vertical menu of key → label → command triples, useful for grouping rarely-used bindings behind a single prefix key. Both have been polished across 3.43.6: -T titles, -B to keep the popup on close, -M to force-enable mouse inside a menu (3.6), and -x/-y accept format expansions (3.6).

text
# ~/.config/tmux/tmux.conf — popup launchers
bind g display-popup -E -w 90% -h 90% -T " lazygit " "lazygit"
bind T display-popup -E -w 80% -h 70% -d "#{pane_current_path}"

# Small actions menu (prefix m)
bind m display-menu -T "#[align=centre] Actions " -x C -y C \
  "Reload config"  r  "source-file ~/.config/tmux/tmux.conf" \
  "Rename window"  n  "command-prompt -I '#W' 'rename-window %%'" \
  "Kill pane"      k  "kill-pane" \
  ""                                                            \
  "Close menu"     q  ""

Output: (none — config snippet)

Flags worth knowing for display-popup:

FlagEffect
-EExit popup when the command exits
-w 80% / -h 70%Size as percentage of the client area
-x C -y CCentre on screen (numbers / formats also work)
-T " title "Border title (3.4+)
-d <dir>Start the command in <dir> (e.g. #{pane_current_path})
-BDon't close on key — wait for the command to exit

Hooks

A hook runs a tmux command whenever a named event fires — pane creation, window rename, client attach, layout change, and so on. Use hooks to enforce conventions (e.g. always renumber windows, auto-rename based on cwd) or wire integrations (e.g. notify on bell). In tmux 3.6 many session-scope hooks were split into pane and window scopes, which means the option you set-hook against has to match the event source.

text
# ~/.config/tmux/tmux.conf

# Rename window to current directory's basename whenever the pane changes path
set-hook -g pane-focus-in 'rename-window "#{b:pane_current_path}"'

# Run a notifier on every command error (3.6+)
set-hook -g command-error 'display-message "tmux: command failed"'

# Per-window hook (3.6+ scope change)
set-hook -w window-renamed 'display-message "renamed to #W"'

# Per-pane hook (3.6+ scope change)
set-hook -p pane-died 'display-message "pane #P died"'

Output: (none — config snippet)

The most useful event names: client-attached, client-detached, session-created, session-renamed, window-linked, window-renamed, window-layout-changed, pane-focus-in, pane-focus-out, pane-died, pane-mode-changed, and command-error (3.6+). Run tmux show-hooks -g to list the ones currently set.

Scripting tmux

Every interactive keybinding is also a CLI subcommand, so a fresh layout can be reconstructed by a script — handy for project-specific environments. Use tmux send-keys to type commands into a pane.

bash
#!/usr/bin/env bash
# Open the "work" session with three windows pre-loaded
SESSION=work

tmux has-session -t "$SESSION" 2>/dev/null && {
  tmux attach -t "$SESSION"
  exit 0
}

tmux new-session  -d -s "$SESSION" -n editor -c /home/alice/project
tmux send-keys    -t "$SESSION:editor" 'vim .' C-m

tmux new-window   -t "$SESSION:" -n server -c /home/alice/project
tmux send-keys    -t "$SESSION:server" 'npm run dev' C-m

tmux new-window   -t "$SESSION:" -n shell  -c /home/alice/project

tmux select-window -t "$SESSION:editor"
tmux attach        -t "$SESSION"

Output: (none — exits 0 on success)

Common pitfalls

  1. Prefix collisions with the shell or editorCtrl-b is used by readline (move back one character); Ctrl-a is used by readline as "go to start of line". Many tmux users rebind to Ctrl-Space or Ctrl-q to avoid both.
  2. Colours look wrong inside tmux — set default-terminal "tmux-256color" and add a Tc override in terminal-overrides to pass 24-bit colour through.
  3. Esc feels laggy in vimset -sg escape-time 10 (down from default 500ms) fixes it.
  4. Lost text selection — mouse mode steals the drag-selection gesture. Hold Shift (or Option on macOS) to bypass tmux and use the terminal's own selection.
  5. Nested tmux — SSHing from one tmux session into another leaves you with two prefix keys to mash through. Use the $TMUX env var to detect nesting; some users bind a second prefix that only triggers when nested.
  6. Pane numbering starts at 0prefix 0 is the first window. Set base-index 1 and pane-base-index 1 if you prefer 1-indexed (matches the keyboard row).
  7. tmux kill-server kills everything — including unsaved work in every session. Always prefer kill-session -t NAME unless you really mean it.

Real-world recipes

Persistent dev session that survives SSH disconnects

The canonical workflow: SSH in, attach (or create) the dev session, work in it. When the connection drops, processes keep running; reconnect and you're back exactly where you were.

bash
# On the remote, in ~/.bashrc
if [[ -z "$TMUX" && -n "$SSH_CONNECTION" ]]; then
  exec tmux new-session -A -s dev
fi

Output:

text
# After ssh alicedev@myhost
[work session attached]

Quick split-pane debugging layout

Three-pane "investigate this server" layout from one keystroke: editor on the left, server logs top-right, shell bottom-right.

bash
tmux new-session -d -s debug -c /home/alice/app
tmux send-keys   -t debug 'vim .' C-m
tmux split-window -h -t debug -c /home/alice/app
tmux send-keys   -t debug 'journalctl -u myapp -f' C-m
tmux split-window -v -t debug -c /home/alice/app
tmux attach      -t debug

Output: (none — exits 0 on success)

Synchronise input across panes

setw synchronize-panes on sends every keystroke to all panes in the current window — useful for running the same command on a row of SSH connections to a fleet of servers.

bash
# In each pane, ssh to a different host
# Then in any pane:
#   prefix :setw synchronize-panes on
# Type once, executed everywhere:
apt list --upgradable

#   prefix :setw synchronize-panes off
# Back to normal

Output: (none — interactive)

Capture pane contents to a file

capture-pane dumps a pane's visible (or full-history) text to a tmux buffer, which can then be saved to a file. The cleanest way to grab the output of a long-running command after the fact.

bash
# From any other pane
tmux capture-pane -pS -3000 -t debug:0.0 > /tmp/pane.log

Output:

text
$ npm run build
> build
> tsc && vite build
✓ built in 1.42s

Plugin management with tpm

The Tmux Plugin Manager (tpm) installs and updates plugins listed in tmux.conf. Popular plugins include tmux-resurrect (save/restore sessions across reboots) and tmux-continuum (auto-save every 15 min).

bash
# Install tpm
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

Output: (none — exits 0 on success)

Then append the plugin list to ~/.tmux.conf:

text
# ~/.tmux.conf — append at end
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'

set -g @continuum-restore 'on'

run '~/.tmux/plugins/tpm/tpm'

Output: (none — config snippet)

Inside tmux, install with prefix I (capital i), update with prefix U, uninstall with prefix Alt-u.

Other plugins worth knowing in 2026:

  • christoomey/vim-tmux-navigator — unifies Ctrl-h/j/k/l between vim/neovim and tmux panes; the navigation feels native.
  • catppuccin/tmux — the most-popular themed status bar; pairs with Catppuccin terminal palettes.
  • tmux-plugins/tmux-yank — bridges the tmux paste-buffer to the system clipboard automatically (no manual pbcopy/xclip binding).
  • tmux-plugins/tmux-fpp — opens any file path on the current pane in $EDITOR with one keystroke.
bash
# Reload the config to load tpm itself
tmux source-file ~/.tmux.conf

Output:

text
TMUX environment reloaded.
Done, press ENTER to continue.

Cheat-card: most-used keys

A printable summary of the bindings to internalise first.

BindingAction
prefix dDetach (leave running)
prefix cNew window
prefix ,Rename window
prefix n / prefix pNext / previous window
prefix 0…9Jump to window N
prefix %Vertical split
prefix "Horizontal split
prefix arrowMove between panes
prefix zToggle pane zoom
prefix xKill pane
prefix [Copy-mode
prefix ]Paste buffer
prefix :Command prompt
prefix ?Show all bindings

prefix ? opens an interactive list of every binding tmux knows — the fastest way to remember a key you've forgotten without leaving the session.

Sources