tmux: Terminal Multiplexing

Why This Matters

You are deploying a critical database migration over SSH to a production server. The script will take 45 minutes to run. Twenty minutes in, your Wi-Fi drops. Your SSH session dies. The migration script -- was it still running? Was it halfway through altering a table? You have no way to know, no way to reconnect to that session, and now you are staring at a potential data corruption scenario.

This exact nightmare is why tmux exists.

tmux (terminal multiplexer) lets you create persistent terminal sessions that survive disconnections. When your SSH connection drops, the tmux session keeps running on the server. You reconnect, reattach, and everything is exactly where you left it -- running processes, command history, output scrollback, all of it.

But tmux is far more than an insurance policy against dropped connections. It lets you split your terminal into multiple panes, manage multiple windows (like tabs), and run several workflows simultaneously in a single SSH connection. Instead of opening five SSH sessions to the same server, you open one and use tmux to organize your workspace.


Try This Right Now

# Install tmux
# Debian/Ubuntu
sudo apt install tmux

# Fedora/RHEL
sudo dnf install tmux

# Arch
sudo pacman -S tmux

# Start a new tmux session
tmux

# You are now inside tmux. Notice the green status bar at the bottom.
# Type a command:
top

# Now detach from the session: press Ctrl-b, then d
# (That means: hold Ctrl, press b, release both, then press d)

# You are back at your regular shell. top is still running inside tmux.
# Reattach:
tmux attach

You just experienced the core superpower of tmux: detach and reattach. The top process kept running even though you "left."


Core Concepts: Sessions, Windows, and Panes

tmux organizes your work in a three-level hierarchy:

+-------------------------------------------------------+
|                   tmux SERVER                          |
|  (background process managing everything)             |
|                                                        |
|  +--------------------------------------------------+ |
|  |  SESSION: "webserver"                             | |
|  |                                                    | |
|  |  +-------------------+  +-------------------+     | |
|  |  | WINDOW 0: "edit"  |  | WINDOW 1: "logs"  |     | |
|  |  |                   |  |                   |     | |
|  |  | +------+--------+ |  | +---------------+ |     | |
|  |  | | PANE | PANE   | |  | |     PANE      | |     | |
|  |  | |  0   |   1    | |  | |      0        | |     | |
|  |  | | vim  | shell  | |  | | tail -f log   | |     | |
|  |  | +------+--------+ |  | +---------------+ |     | |
|  |  +-------------------+  +-------------------+     | |
|  +--------------------------------------------------+ |
|                                                        |
|  +--------------------------------------------------+ |
|  |  SESSION: "database"                              | |
|  |  ...                                               | |
|  +--------------------------------------------------+ |
+-------------------------------------------------------+

Session -- A collection of windows. Think of it as a workspace. You might have a "webserver" session and a "database" session.

Window -- A full-screen view within a session. Like tabs in a browser. Each window has a name shown in the status bar.

Pane -- A subdivision of a window. You can split a window horizontally or vertically into multiple panes, each running its own shell.


The Prefix Key

Almost every tmux command starts with a prefix key combination. The default prefix is:

Ctrl-b

You press Ctrl-b, release it, then press the command key. This two-step process prevents tmux keybindings from interfering with normal typing.

Throughout this chapter, I will write prefix commands as Ctrl-b <key>. For example, Ctrl-b d means: press Ctrl-b, release, press d.

Think About It: Why does tmux use a prefix key instead of direct shortcuts like Ctrl-d for detach? Consider that tmux runs inside a terminal where programs like vim, bash, and python all have their own keybindings. The prefix creates a clean namespace.


Managing Sessions

Creating Sessions

# Start a new unnamed session
tmux

# Start a named session
tmux new-session -s webserver

# Short form
tmux new -s webserver

# Start a session and immediately run a command
tmux new -s monitoring 'htop'

Listing Sessions

# From outside tmux
tmux list-sessions
tmux ls

# Example output:
# database: 1 windows (created Sat Feb 21 10:30:00 2026)
# webserver: 3 windows (created Sat Feb 21 09:15:00 2026) (attached)

The (attached) label tells you which session is currently active.

Attaching and Detaching

# Detach from current session (from inside tmux)
# Ctrl-b d

# Attach to the most recent session
tmux attach
tmux a

# Attach to a specific session
tmux attach -t webserver
tmux a -t database

# Attach to a session, detaching it from any other client first
tmux a -dt webserver

Switching and Killing Sessions

Ctrl-b s        List all sessions and switch interactively
Ctrl-b (        Switch to the previous session
Ctrl-b )        Switch to the next session
# Kill a specific session
tmux kill-session -t database

# Kill all sessions except "webserver"
tmux kill-session -a -t webserver

# Kill the tmux server (destroys everything)
tmux kill-server

WARNING: tmux kill-server destroys ALL sessions, windows, and panes immediately. Any running processes inside tmux will receive SIGHUP and typically terminate. Use this only when you truly want to clean up everything.

Hands-On: Session Management

Let us create a realistic multi-session setup:

# Create a session for web server work
tmux new -s web -d          # -d starts it detached

# Create a session for database work
tmux new -s db -d

# Create a session for monitoring
tmux new -s monitor -d

# List them
tmux ls
# Output:
# db: 1 windows (created ...)
# monitor: 1 windows (created ...)
# web: 1 windows (created ...)

# Attach to the web session
tmux a -t web

# Inside the session, switch between them:
# Ctrl-b s    (shows an interactive list, use arrows and Enter)

# Detach
# Ctrl-b d

# Clean up
tmux kill-session -t db
tmux kill-session -t monitor
tmux kill-session -t web

Managing Windows

Windows are like tabs within a session.

Window Commands

Key BindingAction
Ctrl-b cCreate a new window
Ctrl-b ,Rename the current window
Ctrl-b nSwitch to the next window
Ctrl-b pSwitch to the previous window
Ctrl-b 0-9Switch to window by number
Ctrl-b wList all windows interactively
Ctrl-b &Kill the current window (with confirmation)
Ctrl-b lToggle to the last active window

The Status Bar

The bottom of the screen shows your windows:

[web] 0:edit* 1:logs- 2:shell                    "hostname" 14:30 21-Feb
  ^     ^       ^       ^                             ^         ^
  |     |       |       |                             |         |
session |    previous  other                       hostname   time
name    |    window    window
     current
     window (*)

The * marks the active window. The - marks the previously active window.

Hands-On: Working with Windows

tmux new -s practice

Inside the session:

Ctrl-b c              Create a new window (you're now in window 1)
Ctrl-b ,              Rename it -- type "logs" and press Enter
Ctrl-b c              Create another window (window 2)
Ctrl-b ,              Rename it -- type "editor"
Ctrl-b 0              Switch to window 0
Ctrl-b ,              Rename it -- type "shell"
Ctrl-b w              List all windows -- navigate and select one
Ctrl-b n              Next window
Ctrl-b p              Previous window

Managing Panes

Panes let you see and work with multiple terminals side by side.

Pane Commands

Key BindingAction
Ctrl-b %Split the current pane vertically (left/right)
Ctrl-b "Split the current pane horizontally (top/bottom)
Ctrl-b arrowMove between panes using arrow keys
Ctrl-b oCycle to the next pane
Ctrl-b ;Toggle to the last active pane
Ctrl-b zZoom/unzoom the current pane (full screen toggle)
Ctrl-b xKill the current pane (with confirmation)
Ctrl-b {Swap with the previous pane
Ctrl-b }Swap with the next pane
Ctrl-b SpaceCycle through pane layouts
Ctrl-b qShow pane numbers (press number to jump to that pane)

Resizing Panes

Ctrl-b Ctrl-arrow     Resize pane in direction of arrow (1 cell at a time)
Ctrl-b Alt-arrow      Resize pane in direction of arrow (5 cells at a time)

Or use the command line:

Ctrl-b :              Enter command mode
resize-pane -D 10     Resize down by 10 cells
resize-pane -U 5      Resize up by 5 cells
resize-pane -L 10     Resize left by 10 cells
resize-pane -R 10     Resize right by 10 cells

Hands-On: Building a Dashboard

Let us create a practical monitoring layout:

tmux new -s dashboard
# Split vertically (left and right)
Ctrl-b %

# In the right pane, split horizontally (top and bottom)
Ctrl-b "

# Now you have three panes:
# +------------------+------------------+
# |                  |                  |
# |     pane 0       |     pane 1       |
# |                  |                  |
# |                  +------------------+
# |                  |                  |
# |                  |     pane 2       |
# |                  |                  |
# +------------------+------------------+

# Navigate to pane 0 (left)
Ctrl-b Left

# Run a process in each pane:
# Pane 0: system monitoring
htop

# Ctrl-b Right to go to pane 1
Ctrl-b Right
# Run log watching
tail -f /var/log/syslog    # or journalctl -f on systemd systems

# Ctrl-b Down to go to pane 2
Ctrl-b Down
# Free for commands

To zoom into any pane temporarily:

Ctrl-b z         Zoom current pane to full window
Ctrl-b z         Press again to unzoom back to the layout

Copy Mode: Scrolling and Copying Text

By default, you cannot scroll up in tmux using your mouse scroll wheel (unless you enable mouse mode). Instead, tmux has a copy mode.

Entering and Using Copy Mode

Ctrl-b [         Enter copy mode
q                Exit copy mode

In copy mode, you can scroll and navigate:

Arrow keys       Move cursor
Page Up/Down     Scroll by page
g                Go to top of buffer
G                Go to bottom of buffer
/pattern         Search forward
?pattern         Search backward
n                Next search match
N                Previous search match

Copying Text

tmux supports two key binding styles: emacs (default) and vi. With the default emacs bindings:

Ctrl-b [         Enter copy mode
                 Navigate to the start of text you want
Ctrl-Space       Start selection
                 Navigate to the end of text
Alt-w            Copy selection (or Ctrl-w to cut)
Ctrl-b ]         Paste the copied text

With vi-style bindings (add set-window-option -g mode-keys vi to .tmux.conf):

Ctrl-b [         Enter copy mode
                 Navigate to the start of text
Space            Start selection
                 Navigate to the end
Enter            Copy selection
Ctrl-b ]         Paste

Think About It: When would you use tmux copy mode instead of just piping output to a file? Consider cases where a program's output has already scrolled past and you need to grab something from the scrollback buffer.


Practical Workflows

Workflow 1: SSH + tmux for Remote Work

This is the single most important tmux workflow. It protects you from disconnections.

# SSH into a remote server
ssh user@production-server

# Start a named tmux session
tmux new -s deploy

# Run your long-running deployment
./deploy.sh

# If you need to disconnect (or if your connection drops):
# Ctrl-b d

# Later, reconnect:
ssh user@production-server
tmux a -t deploy
# Everything is exactly as you left it

Pro tip: Always name your sessions when working on remote servers. If you just run tmux, you will end up with multiple unnamed sessions and forget which is which.

Workflow 2: Development Environment

Set up a complete development workspace with one command:

# Create a script to set up your tmux workspace
cat > ~/tmux-dev.sh << 'EOF'
#!/bin/bash
SESSION="dev"

# Create session with first window named "editor"
tmux new-session -d -s $SESSION -n editor

# Window 0: editor
tmux send-keys -t $SESSION:0 'vim .' C-m

# Window 1: server
tmux new-window -t $SESSION -n server
tmux send-keys -t $SESSION:1 'echo "Start your server here"' C-m

# Window 2: logs + shell (split pane)
tmux new-window -t $SESSION -n logs
tmux split-window -h -t $SESSION:2
tmux send-keys -t $SESSION:2.0 'journalctl -f' C-m
tmux send-keys -t $SESSION:2.1 'echo "Shell ready"' C-m

# Window 3: git
tmux new-window -t $SESSION -n git
tmux send-keys -t $SESSION:3 'git status' C-m

# Select the first window
tmux select-window -t $SESSION:0

# Attach to the session
tmux attach -t $SESSION
EOF

chmod +x ~/tmux-dev.sh

Workflow 3: Monitoring Multiple Servers

# Split and SSH to different servers in each pane
tmux new -s servers

# Ctrl-b %   (split vertically)
# Ctrl-b "   (split the right pane horizontally)

# In each pane, SSH to a different server:
# Pane 0: ssh web-server-1
# Pane 1: ssh web-server-2
# Pane 2: ssh db-server-1

# Synchronize input to all panes (type once, execute everywhere):
Ctrl-b :
setw synchronize-panes on

# Now every keystroke goes to all panes simultaneously!
# Type: uptime
# All three servers show their uptime

# Turn it off when done:
Ctrl-b :
setw synchronize-panes off

WARNING: Synchronized panes means EVERY keystroke goes to ALL panes. If you type sudo rm -rf / in synchronized mode, it executes on every server. Be extremely careful. Always turn off synchronization when you are done with the parallel command.


Customizing tmux: .tmux.conf

tmux reads its configuration from ~/.tmux.conf. Here is a practical starting configuration:

vim ~/.tmux.conf
# ============================================
# General Settings
# ============================================

# Use 256 colors
set -g default-terminal "screen-256color"

# Increase scrollback buffer
set -g history-limit 50000

# Start window numbering at 1 (not 0)
set -g base-index 1
setw -g pane-base-index 1

# Renumber windows when one is closed
set -g renumber-windows on

# Reduce escape delay (important for Vim users)
set -sg escape-time 10

# Enable mouse support
set -g mouse on

# ============================================
# Key Bindings
# ============================================

# Remap prefix to Ctrl-a (easier to reach than Ctrl-b)
# unbind C-b
# set -g prefix C-a
# bind C-a send-prefix

# Split panes with | and - (more intuitive)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
unbind '"'
unbind %

# New windows open in the current directory
bind c new-window -c "#{pane_current_path}"

# Reload config with prefix + r
bind r source-file ~/.tmux.conf \; display "Config reloaded!"

# Use vi keys in copy mode
setw -g mode-keys vi

# ============================================
# Pane Navigation (Vim-style)
# ============================================

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# Resize panes with Shift+arrow
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# ============================================
# Status Bar
# ============================================

# Status bar colors
set -g status-style bg=colour235,fg=colour136

# Current window highlight
setw -g window-status-current-style fg=colour166,bold

# Status bar content
set -g status-left '#[fg=colour46]#S #[fg=colour245]| '
set -g status-right '#[fg=colour245]%Y-%m-%d #[fg=colour136]%H:%M '
set -g status-left-length 20

After saving, reload the config:

Ctrl-b :
source-file ~/.tmux.conf

Or if you added the bind r shortcut: Ctrl-b r.

Distro Note: On older systems with tmux < 2.1, some settings like set -g mouse on do not exist. Older versions used separate mouse-select-pane, mouse-resize-pane, and mouse-select-window options. Check your tmux version with tmux -V.


tmux Command Reference

You can run tmux commands from the command line or from within tmux (via Ctrl-b :).

# Session commands
tmux new -s name              Create named session
tmux ls                       List sessions
tmux a -t name                Attach to session
tmux kill-session -t name     Kill session

# Window commands (from command mode Ctrl-b :)
new-window -n name            Create named window
rename-window name            Rename current window
kill-window                   Kill current window

# Pane commands (from command mode)
split-window -h               Split horizontally
split-window -v               Split vertically
resize-pane -D/U/L/R N       Resize pane by N cells

# Misc
list-keys                     Show all key bindings
show-options -g               Show global options
display-message "text"        Show a message

Debug This

You run tmux a and get:

no sessions

But you are sure you started one earlier today. What happened?

Diagnosis:

  1. The tmux server may have been restarted (e.g., system reboot). tmux sessions do not survive reboots by default -- they live in memory.

  2. Someone (or a cron job) may have run tmux kill-server.

  3. The session may have exited naturally. If the last shell in a session exits, the session closes. If you ran tmux new -s work 'python script.py' and the script finished, the session is gone.

Prevention:

  • For long-running work, make sure the session has a regular shell, not just a single command.
  • Consider tmux session persistence plugins like tmux-resurrect or tmux-continuum that can save and restore sessions across reboots.
  • Check systemctl status tmux if your system runs tmux as a systemd service.

What Just Happened?

+------------------------------------------------------------------+
|                        CHAPTER RECAP                              |
+------------------------------------------------------------------+
|                                                                   |
|  tmux creates PERSISTENT terminal sessions that survive          |
|  disconnections -- essential for remote server work.             |
|                                                                   |
|  Hierarchy: Session > Window > Pane                              |
|                                                                   |
|  PREFIX KEY: Ctrl-b (then command key)                           |
|                                                                   |
|  Sessions: new -s name, attach -t name, Ctrl-b d to detach      |
|  Windows:  Ctrl-b c (create), Ctrl-b n/p (next/prev)            |
|  Panes:    Ctrl-b % (v-split), Ctrl-b " (h-split)               |
|            Ctrl-b arrow (navigate), Ctrl-b z (zoom)              |
|                                                                   |
|  Copy mode: Ctrl-b [ to enter, scroll/search/copy               |
|                                                                   |
|  Customize with ~/.tmux.conf                                     |
|                                                                   |
|  Key workflow: SSH -> tmux new -s name -> work -> Ctrl-b d       |
|                SSH -> tmux a -t name -> resume exactly            |
|                                                                   |
+------------------------------------------------------------------+

Try This

  1. Session Persistence Test: Start a tmux session, run ping 8.8.8.8, detach, wait 30 seconds, reattach. Verify the ping is still running and count the packets.

  2. Window Workflow: Create a session with 4 windows named "edit", "build", "test", and "deploy". Practice switching between them using numbers and the interactive list.

  3. Pane Layout: Create a 4-pane layout (2x2 grid) in a single window. Run a different monitoring command in each pane: top, iostat 1, watch df -h, and journalctl -f.

  4. Synchronized Panes: If you have access to multiple machines (or use localhost), set up synchronized panes to run the same command on three "servers" simultaneously.

  5. tmux Scripting: Write a shell script that creates your ideal development environment with named windows, split panes, and commands pre-typed in each pane. Make it idempotent (check if the session exists before creating it).

  6. Bonus Challenge: Customize your ~/.tmux.conf to use Ctrl-a as the prefix key (screen-style), add vim-style pane navigation, and make the status bar show system load. Share your config with a colleague and have them try it.