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-dfor 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-serverdestroys 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 Binding | Action |
|---|---|
Ctrl-b c | Create a new window |
Ctrl-b , | Rename the current window |
Ctrl-b n | Switch to the next window |
Ctrl-b p | Switch to the previous window |
Ctrl-b 0-9 | Switch to window by number |
Ctrl-b w | List all windows interactively |
Ctrl-b & | Kill the current window (with confirmation) |
Ctrl-b l | Toggle 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 Binding | Action |
|---|---|
Ctrl-b % | Split the current pane vertically (left/right) |
Ctrl-b " | Split the current pane horizontally (top/bottom) |
Ctrl-b arrow | Move between panes using arrow keys |
Ctrl-b o | Cycle to the next pane |
Ctrl-b ; | Toggle to the last active pane |
Ctrl-b z | Zoom/unzoom the current pane (full screen toggle) |
Ctrl-b x | Kill the current pane (with confirmation) |
Ctrl-b { | Swap with the previous pane |
Ctrl-b } | Swap with the next pane |
Ctrl-b Space | Cycle through pane layouts |
Ctrl-b q | Show 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 ondo not exist. Older versions used separatemouse-select-pane,mouse-resize-pane, andmouse-select-windowoptions. Check your tmux version withtmux -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:
-
The tmux server may have been restarted (e.g., system reboot). tmux sessions do not survive reboots by default -- they live in memory.
-
Someone (or a cron job) may have run
tmux kill-server. -
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-resurrectortmux-continuumthat can save and restore sessions across reboots. - Check
systemctl status tmuxif 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
-
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. -
Window Workflow: Create a session with 4 windows named "edit", "build", "test", and "deploy". Practice switching between them using numbers and the interactive list.
-
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, andjournalctl -f. -
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. -
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).
-
Bonus Challenge: Customize your
~/.tmux.confto useCtrl-aas 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.