tmux for Linux Developers: Complete Productivity Guide

Why tmux Matters for Developers
If you've ever lost hours of terminal state to an SSH disconnect, accidentally closed a terminal window mid-compilation, or fumbled between a dozen open terminal tabs to find your test runner, tmux solves all of these problems simultaneously. It's not just a terminal multiplexer — it's a session persistence layer that fundamentally changes how you interact with the command line.
Three capabilities define why tmux is indispensable:
- Persistent sessions: Your tmux session lives on the server, not in your terminal emulator. Close your laptop, lose your SSH connection, reboot your local machine — your remote processes keep running. Reconnect and everything is exactly where you left it.
- Multiplexed views: Split any terminal into arbitrary panes. Run your editor, test suite, build output, and log tail simultaneously in a single window without switching applications.
- Remote work survival: On a slow or unreliable connection, tmux is a lifeline. One SSH connection supports your entire multi-pane workspace, and
moshcombined with tmux makes network interruptions completely invisible.
Installing tmux
tmux is available in every major distribution's package manager. Install it with the appropriate command for your system:
# Debian / Ubuntu
sudo apt update && sudo apt install tmux
# Fedora / RHEL / CentOS (dnf)
sudo dnf install tmux
# Arch Linux / Manjaro
sudo pacman -S tmux
# macOS (for reference)
brew install tmux
# Verify installation
tmux -VFor a recent version on older LTS systems, compile from source or use a static binary. Ubuntu 20.04 ships tmux 3.0; the current release is 3.4 with significant improvements to mouse support and popup windows.
Core Concepts: The Mental Model
Understanding tmux's hierarchy is critical. Many people use tmux for years with a broken mental model, which leads to confusion and inefficiency.
The Hierarchy
tmux operates as a client-server architecture:
- Server: A background process (
tmux) that owns all sessions, windows, and panes. It starts automatically when you launch your first session and stops when the last session exits. - Session: A named collection of windows. Think of a session as a project — one session for your API backend, another for your frontend, another for infrastructure work. Sessions persist when you detach.
- Window: Occupies the full terminal area. Analogous to a browser tab. A session can have many windows, each with its own purpose (editor, shell, database CLI).
- Pane: A subdivision of a window. Windows are split into panes, each running an independent shell process. This is where the actual multiplexing happens.
Server
└── Session: "backend"
├── Window 0: "editor" → [pane 0: nvim] [pane 1: terminal]
├── Window 1: "tests" → [pane 0: test runner]
└── Window 2: "docker" → [pane 0: docker logs] [pane 1: shell]This hierarchy is not just conceptual — every tmux command targets a specific point in this tree. Understanding it makes the command syntax logical rather than arbitrary.
Essential Keybindings
Every tmux command is prefixed with a key combination called the prefix. The default prefix is Ctrl+b. You press the prefix, release it, then press the command key. This two-step process is intentional — it prevents tmux from intercepting keys meant for your applications.
Prefix and Command Mode
Ctrl+b → prefix (default)
Ctrl+b : → enter command mode (type tmux commands directly)Session Commands
| Keys | Action |
|---|---|
Ctrl+b d | Detach from current session |
Ctrl+b s | List and switch sessions interactively |
Ctrl+b $ | Rename current session |
Ctrl+b ( | Switch to previous session |
Ctrl+b ) | Switch to next session |
Window Commands
| Keys | Action |
|---|---|
Ctrl+b c | Create new window |
Ctrl+b , | Rename current window |
Ctrl+b w | List windows interactively |
Ctrl+b n | Next window |
Ctrl+b p | Previous window |
Ctrl+b 0-9 | Switch to window by number |
Ctrl+b & | Kill current window |
Pane Commands
| Keys | Action |
|---|---|
Ctrl+b % | Split pane vertically (side by side) |
Ctrl+b " | Split pane horizontally (top/bottom) |
Ctrl+b arrow | Move to pane in arrow direction |
Ctrl+b q | Show pane numbers (press number to jump) |
Ctrl+b z | Toggle pane zoom (fullscreen) |
Ctrl+b x | Kill current pane |
Ctrl+b { | Move pane left |
Ctrl+b } | Move pane right |
Ctrl+b Ctrl+arrow | Resize pane by 1 cell |
Ctrl+b Alt+arrow | Resize pane by 5 cells |
Ctrl+b Space | Cycle through predefined layouts |
Copy Mode
Ctrl+b [ # Enter copy mode (scroll with arrow keys or PgUp/PgDn)
q # Exit copy mode
Space # Start selection (in copy mode)
Enter # Copy selection
Ctrl+b ] # Paste bufferSession Management
Creating and Naming Sessions
# Start a new named session
tmux new-session -s backend
# Short form
tmux new -s frontend
# Start a session and run a command
tmux new -s logs -d 'tail -f /var/log/syslog'
# List all sessions
tmux list-sessions
tmux lsDetaching and Attaching
# Detach from inside tmux
Ctrl+b d
# Attach to a specific session
tmux attach-session -t backend
tmux a -t backend
# Attach to most recent session
tmux a
# Attach and detach any other clients (forces single view)
tmux a -t backend -dSharing Sessions
tmux supports multiple clients connecting to the same session simultaneously — useful for pair programming over SSH:
# Both users connect to the same server and attach to the same session
# User 1 creates the session
tmux new -s pairing
# User 2 attaches to it (requires same Unix user or socket permissions)
tmux a -t pairing
# For different users, use a shared socket
tmux -S /tmp/shared-session new -s collab
chmod 777 /tmp/shared-session
# Other user:
tmux -S /tmp/shared-session attachWindow Workflow
The most productive tmux users organize windows by task type within a project session, not by file:
# Create a window with a name immediately
tmux new-window -n "editor"
tmux new-window -n "tests"
tmux new-window -n "git"
# Rename current window from inside tmux
Ctrl+b ,
# Move window to position 3
Ctrl+b : move-window -t 3
# Move window to another session
Ctrl+b : move-window -t other-session:
# Link a window (shared between sessions — useful for monitoring)
tmux link-window -s monitoring:logs -t backend:logsPane Layouts
Standard Developer Layouts
tmux has five built-in layouts accessible with Ctrl+b Space: even-horizontal, even-vertical, main-horizontal, main-vertical, and tiled. For most development work, custom splits are more useful:
# Side-by-side: editor left, tests right
tmux split-window -h # Split current pane horizontally
# Result: [editor | tests]
# Three-pane dev setup: editor top-left, terminal bottom-left, logs right
tmux split-window -h # Creates right pane
tmux select-pane -t 0 # Go back to left pane
tmux split-window -v # Split left pane vertically
# Result:
# [editor | logs ]
# [terminal | ]
# Resize panes precisely
tmux resize-pane -t 0 -x 120 # Set pane 0 to 120 columns wide
tmux resize-pane -D 10 # Resize current pane down 10 linesApplying a Preset Layout
# Apply main-vertical: one large pane on the left, stack of small ones on right
tmux select-layout main-vertical
# Set the main pane width for main-vertical/horizontal
tmux set-option main-pane-width 160
tmux set-option main-pane-height 40The ~/.tmux.conf File
The default tmux configuration is functional but not ergonomic. Here is a complete, well-commented ~/.tmux.conf for serious development work:
# ~/.tmux.conf — Production developer configuration
# ─── PREFIX ──────────────────────────────────────────────────────────────────
# Change prefix from Ctrl+b to Ctrl+a (screen-style, easier to reach)
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix
# ─── GENERAL ─────────────────────────────────────────────────────────────────
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",xterm-256color:Tc" # True color support
set -g history-limit 50000 # Larger scrollback buffer
set -g display-time 4000 # Status messages display for 4 seconds
set -g status-interval 5 # Refresh status bar every 5 seconds
set -g focus-events on # Enable focus events (used by vim autoread)
setw -g aggressive-resize on # Resize to smallest *active* client
set -sg escape-time 0 # No delay for escape key (critical for vim)
set -g base-index 1 # Start window numbering at 1
setw -g pane-base-index 1 # Start pane numbering at 1
set -g renumber-windows on # Renumber windows when one is closed
# ─── MOUSE SUPPORT ───────────────────────────────────────────────────────────
set -g mouse on # Enable mouse for pane selection and resize
# ─── KEY BINDINGS ────────────────────────────────────────────────────────────
# Reload config without restarting
bind r source-file ~/.tmux.conf ; display-message "Config reloaded!"
# More intuitive split keys (| and - instead of % and ")
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
unbind '"'
unbind %
# New window keeps current path
bind c new-window -c "#{pane_current_path}"
# Vim-style pane navigation (works with vim-tmux-navigator plugin)
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# Vim-style pane resizing
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
# Quick pane switching with Alt+arrow (no prefix needed)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# Quick window switching
bind -n S-Left previous-window
bind -n S-Right next-window
# Kill without confirmation
bind X kill-pane
bind Q kill-window
# ─── COPY MODE (VI STYLE) ────────────────────────────────────────────────────
setw -g mode-keys vi
bind Enter copy-mode # Enter copy mode with Enter
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi V send -X select-line
bind -T copy-mode-vi C-v send -X rectangle-toggle
bind -T copy-mode-vi y send -X copy-selection-and-cancel
bind -T copy-mode-vi Escape send -X cancel
# System clipboard integration (requires xclip or xsel on Linux)
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "xclip -in -selection clipboard"
# On Wayland, use: "wl-copy"
# On macOS, use: "pbcopy"
# ─── STATUS BAR ──────────────────────────────────────────────────────────────
set -g status on
set -g status-position bottom
set -g status-justify left
set -g status-style 'bg=#1e2030 fg=#c8d3f5'
# Left: session name
set -g status-left-length 40
set -g status-left '#[bg=#82aaff,fg=#1e2030,bold] #S #[bg=#1e2030,fg=#82aaff]'
# Right: user@host, date, time
set -g status-right-length 100
set -g status-right '#[fg=#636da6] %Y-%m-%d #[fg=#82aaff] %H:%M #[bg=#82aaff,fg=#1e2030,bold] #(whoami)@#h '
# Window status
setw -g window-status-format ' #I:#W '
setw -g window-status-current-format '#[bg=#82aaff,fg=#1e2030,bold] #I:#W '
setw -g window-status-separator ''
# ─── PANE BORDERS ────────────────────────────────────────────────────────────
set -g pane-border-style 'fg=#363b54'
set -g pane-active-border-style 'fg=#82aaff'
# ─── PLUGINS (TPM) ───────────────────────────────────────────────────────────
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 @plugin 'tmux-plugins/tmux-yank'
set -g @plugin 'christoomey/vim-tmux-navigator'
# Plugin settings
set -g @resurrect-capture-pane-contents 'on'
set -g @resurrect-strategy-nvim 'session' # Restore nvim sessions
set -g @continuum-restore 'on' # Auto-restore on server start
set -g @continuum-save-interval '10' # Save every 10 minutes
# Initialize TPM (must be last line)
run '~/.tmux/plugins/tpm/tpm'tmux Plugin Manager (TPM)
Installing TPM
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
# Source your config
tmux source ~/.tmux.conf
# Install plugins: inside tmux, press prefix + I (capital i)
Ctrl+a IEssential Plugins
tmux-resurrect saves and restores your entire session layout — windows, panes, working directories, and running programs:
Ctrl+a Ctrl+s # Save session
Ctrl+a Ctrl+r # Restore sessiontmux-continuum automates resurrect saves on a timer and triggers automatic restore when the tmux server starts. Combined with resurrect, your environment survives reboots completely transparently.
tmux-yank makes copy mode work with the system clipboard automatically, solving the perennial problem of getting text out of tmux into other applications.
Managing Plugins
Ctrl+a I # Install new plugins listed in .tmux.conf
Ctrl+a U # Update all plugins
Ctrl+a Alt+u # Remove plugins no longer in .tmux.confScripting tmux: Automated Dev Environments
Raw tmux Scripting
You can script an entire development environment using tmux commands. This is faster than tmuxinator for simple setups and has zero dependencies:
#!/usr/bin/env bash
# dev-session.sh — Launch a full development environment
SESSION="myapp"
WORKDIR="$HOME/projects/myapp"
# Exit if session already exists
tmux has-session -t "$SESSION" 2>/dev/null
if [ $? -eq 0 ]; then
echo "Session $SESSION already exists. Attaching..."
tmux attach-session -t "$SESSION"
exit 0
fi
# Create session with first window named "editor"
tmux new-session -d -s "$SESSION" -n "editor" -c "$WORKDIR"
# Window 1: Editor — nvim on the left, shell on the right
tmux send-keys -t "$SESSION:editor" "nvim ." Enter
tmux split-window -h -t "$SESSION:editor" -c "$WORKDIR"
tmux resize-pane -t "$SESSION:editor.1" -x 50 # Right pane 50 cols wide
# Window 2: Tests
tmux new-window -t "$SESSION" -n "tests" -c "$WORKDIR"
tmux send-keys -t "$SESSION:tests" "npm test -- --watch" Enter
# Window 3: Server + logs (two panes)
tmux new-window -t "$SESSION" -n "server" -c "$WORKDIR"
tmux send-keys -t "$SESSION:server" "npm run dev" Enter
tmux split-window -v -t "$SESSION:server" -c "$WORKDIR"
tmux send-keys -t "$SESSION:server.2" "tail -f logs/app.log" Enter
tmux resize-pane -t "$SESSION:server.1" -y 35 # Top pane 35 lines
# Window 4: Git / misc shell
tmux new-window -t "$SESSION" -n "git" -c "$WORKDIR"
tmux send-keys -t "$SESSION:git" "git status" Enter
# Go back to editor window and focus the nvim pane
tmux select-window -t "$SESSION:editor"
tmux select-pane -t "$SESSION:editor.0"
# Attach
tmux attach-session -t "$SESSION"chmod +x dev-session.sh
./dev-session.shUsing tmuxinator
For more complex setups with YAML-based configuration, tmuxinator provides a cleaner syntax:
gem install tmuxinator
# or
sudo apt install tmuxinator# ~/.config/tmuxinator/myapp.yml
name: myapp
root: ~/projects/myapp
windows:
- editor:
layout: main-vertical
panes:
- nvim .
- git status
- tests:
panes:
- npm test -- --watch
- server:
layout: even-vertical
panes:
- npm run dev
- tail -f logs/app.log
- git:
panes:
- git log --oneline -20mux start myapp # Start the session
mux stop myapp # Stop it
mux list # List all projectsIntegration with Vim/Neovim
The vim-tmux-navigator plugin provides seamless navigation between vim splits and tmux panes using the same keybindings — Ctrl+h/j/k/l. This eliminates the mental overhead of knowing whether you're in a vim split or a tmux pane.
Installing in Neovim (lazy.nvim)
-- In your lazy.nvim config
{
"christoomey/vim-tmux-navigator",
cmd = {
"TmuxNavigateLeft", "TmuxNavigateDown",
"TmuxNavigateUp", "TmuxNavigateRight",
},
keys = {
{ "", "TmuxNavigateLeft" },
{ "", "TmuxNavigateDown" },
{ "", "TmuxNavigateUp" },
{ "", "TmuxNavigateRight" },
},
} The matching tmux config (already in .tmux.conf above)
# These bindings are automatically set by vim-tmux-navigator's TPM plugin
# If managing manually:
is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(S+/)?g?(view|n?vim?x?)(diff)?$'"
bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L'
bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D'
bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U'
bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R'Advanced Techniques Worth Knowing
Popup Windows (tmux 3.2+)
# Open a floating shell popup (useful for quick commands)
Ctrl+a :display-popup -E bash
# Bind it to a key
bind g display-popup -E -w 80% -h 80% "lazygit"
bind f display-popup -E -w 80% -h 80% "fzf --preview 'bat --color=always {}'"Synchronizing Panes
# Type the same command in all panes simultaneously
# Useful for deploying to multiple servers
Ctrl+a :setw synchronize-panes on
# Turn it off
Ctrl+a :setw synchronize-panes offSending Commands Programmatically
# Run a command in a specific pane without switching to it
tmux send-keys -t myapp:tests "npm test" Enter
# Clear a pane
tmux send-keys -t myapp:server.0 "C-c" ""
tmux send-keys -t myapp:server.0 "clear" EnterLogging Pane Output
# Pipe pane output to a file
tmux pipe-pane -t myapp:server "cat >> /tmp/server-output.log"
# Stop logging
tmux pipe-pane -t myapp:serverA Complete Four-Pane Development Session Script
This is the complete, copy-paste-ready script for a realistic full-stack development setup. Save it as ~/bin/dev and make it executable:
#!/usr/bin/env bash
# ~/bin/dev — Full-stack development environment launcher
# Usage: dev [project-name] [project-dir]
set -e
SESSION="${1:-dev}"
WORKDIR="${2:-$(pwd)}"
if ! [ -d "$WORKDIR" ]; then
echo "Error: Directory $WORKDIR does not exist"
exit 1
fi
# Attach to existing session if it exists
if tmux has-session -t "$SESSION" 2>/dev/null; then
echo "Attaching to existing session: $SESSION"
tmux attach-session -t "$SESSION"
exit 0
fi
echo "Creating new session: $SESSION in $WORKDIR"
# ── WINDOW 1: Editor (main window) ───────────────────────────────────────────
# Layout: [nvim (large) | terminal (narrow)]
tmux new-session -d -s "$SESSION" -n "editor" -c "$WORKDIR"
# Left pane: open nvim
tmux send-keys -t "$SESSION:editor" "nvim ." Enter
# Right pane: project shell
tmux split-window -h -t "$SESSION:editor" -c "$WORKDIR" -l 45
# ── WINDOW 2: Application server ─────────────────────────────────────────────
# Layout: [server output (top) | log tail (bottom)]
tmux new-window -t "$SESSION" -n "server" -c "$WORKDIR"
tmux send-keys -t "$SESSION:server" "# Start your dev server here" Enter
tmux split-window -v -t "$SESSION:server" -c "$WORKDIR" -l 10
tmux send-keys -t "$SESSION:server.1" "# Logs pane" Enter
# ── WINDOW 3: Tests ───────────────────────────────────────────────────────────
# Layout: [test runner (left) | coverage/output (right)]
tmux new-window -t "$SESSION" -n "tests" -c "$WORKDIR"
tmux send-keys -t "$SESSION:tests" "# Run your test suite here" Enter
tmux split-window -h -t "$SESSION:tests" -c "$WORKDIR"
tmux send-keys -t "$SESSION:tests.1" "# Watch mode or coverage output" Enter
tmux select-pane -t "$SESSION:tests.0"
# ── WINDOW 4: Database / Services ────────────────────────────────────────────
# Layout: three panes — db client top-left, redis cli bottom-left, docker right
tmux new-window -t "$SESSION" -n "services" -c "$WORKDIR"
# Top-left: database
tmux send-keys -t "$SESSION:services" "# psql or mysql CLI" Enter
# Bottom-left: redis or other service
tmux split-window -v -t "$SESSION:services.0" -c "$WORKDIR" -l 12
tmux send-keys -t "$SESSION:services.1" "# redis-cli" Enter
# Right: docker / container status
tmux split-window -h -t "$SESSION:services.0" -c "$WORKDIR"
tmux send-keys -t "$SESSION:services.2" "watch -n 2 docker ps" Enter
# ── FINALIZE ──────────────────────────────────────────────────────────────────
# Return to editor window, focus nvim pane
tmux select-window -t "$SESSION:editor"
tmux select-pane -t "$SESSION:editor.0"
# Attach
tmux attach-session -t "$SESSION"chmod +x ~/bin/dev
# Launch for a specific project
dev myapi ~/projects/myapi
# Launch in current directory
dev myapiTroubleshooting Common Issues
Colors look wrong
# Check what TERM is reported inside tmux
echo $TERM # Should show screen-256color or tmux-256color
# Force 256 color support in your shell profile
export TERM=xterm-256color
# For true color, add to .tmux.conf:
set -ga terminal-overrides ",xterm-256color:Tc"