Persistent Terminal Sessions: A Practical Guide to tmux and zellij
You're running a deployment script on a remote server when your WiFi drops. Or maybe you've kicked off a long-running test suite, closed your laptop to grab lunch, and returned to find the terminal session dead and the process killed. Or perhaps you're running an AI coding agent that's methodically refactoring your codebase when your SSH connection times out.
These scenarios are frustratingly common because terminal sessions are ephemeral by default. When the connection dies or the terminal window closes, everything running in that session dies with it. For quick commands, this is fine. For long-running processes, remote work, or AI agent sessions that might run for hours, it's a dealbreaker.
This is where terminal multiplexers come in. Tools like tmux and zellij create persistent sessions that survive disconnects, crashes, and even server reboots. They're essential infrastructure for professional development workflows, especially when working with AI coding agents that need to maintain state across extended operations.
This guide will teach you how to actually set up and use these tools, from installation to practical workflows.
Why Terminal Sessions Die
Understanding the problem helps clarify why multiplexers matter.
A standard terminal session has a simple lifecycle: you open a terminal window, which spawns a shell process (bash, zsh, fish). That shell is your parent process. Any commands you run become child processes of that shell. When you close the terminal window or lose your SSH connection, the shell process receives a SIGHUP (hangup) signal. By default, this signal cascades to all child processes, killing everything.
This design made sense in the era of physical terminals connected to mainframes. When someone unplugged the terminal cable, the system needed to clean up that user's processes. But in modern development, we regularly work with:
- Remote servers accessed via SSH (connections drop)
- Long-running builds, tests, or deployments (take hours)
- AI coding agents making large-scale changes (can run overnight)
- Development servers that should stay running (need to survive laptop sleep)
- Background processes we want to check on later (logs, monitors)
In all these scenarios, ephemeral sessions are the wrong model. You need sessions that persist independent of your terminal window or network connection.
Enter Terminal Multiplexers
Terminal multiplexers solve this by creating an intermediary layer between your terminal window and your shell processes. When you run tmux or zellij:
- A multiplexer server process starts
- This server creates and manages persistent sessions
- Your terminal window attaches as a client to view the session
- When you disconnect, the server keeps running with all your processes intact
- You can reconnect later and pick up exactly where you left off
The server process runs independent of any terminal window, so your work survives disconnects, crashes, and intentional detaches. You can start a process, disconnect, shut down your laptop, come back hours later, and reattach to find everything still running.
Beyond persistence, multiplexers add powerful workflow features: multiple windows (like terminal tabs), split panes for viewing multiple shells simultaneously, session naming and organization, and scriptable layouts.
tmux: The Industry Standard
tmux (terminal multiplexer) has been the go-to solution for over a decade. It's mature, widely deployed, and has extensive documentation and community resources. If you SSH into most production servers, tmux is likely already installed.
Installing tmux
On macOS:
brew install tmux
On Ubuntu/Debian:
sudo apt install tmux
On CentOS/RHEL:
sudo yum install tmux
Verify installation:
tmux -V
# Should output something like: tmux 3.4
Core Concepts
tmux organizes work hierarchically:
- Sessions are the top level. Each session is a completely independent workspace with its own set of windows and processes. Sessions persist as long as the tmux server is running.
- Windows are like tabs within a session. Each window contains one or more panes.
- Panes are split sections within a window, each running its own shell.
You'll typically create one session per project or major task, use windows for different contexts within that project (code, tests, logs), and split windows into panes for side-by-side viewing.
Essential Commands
Start a new session:
tmux
This creates an unnamed session and attaches you to it. You'll see a status bar at the bottom showing session info.
Create a named session (much better for organization):
tmux new -s api-refactor
Detach from a session (leave it running in the background):
# Inside tmux, press: Ctrl-b, then d
The session continues running. Your terminal returns to normal.
List all running sessions:
tmux ls
Output might look like:
api-refactor: 2 windows (created Thu Feb 6 09:23:15 2026)
testing: 1 window (created Thu Feb 6 10:15:42 2026)
Attach to a session:
tmux attach -t api-refactor
# Or shorthand:
tmux a -t api-refactor
If only one session exists, you can omit the session name:
tmux attach
Kill a session when you're done with it:
tmux kill-session -t api-refactor
Key Bindings Cheat Sheet
tmux uses a prefix key for all commands. By default, this is Ctrl-b. Press the prefix, release, then press the command key.
Sessions:
Ctrl-b d- Detach from sessionCtrl-b s- List and switch sessionsCtrl-b $- Rename current session
Windows:
Ctrl-b c- Create new windowCtrl-b n- Next windowCtrl-b p- Previous windowCtrl-b 0-9- Switch to window by numberCtrl-b ,- Rename current windowCtrl-b &- Kill current window
Panes:
Ctrl-b %- Split pane vertically (side by side)Ctrl-b "- Split pane horizontally (top and bottom)Ctrl-b arrow- Navigate between panesCtrl-b x- Kill current paneCtrl-b z- Zoom current pane (fullscreen toggle)Ctrl-b {- Move pane leftCtrl-b }- Move pane right
Other:
Ctrl-b ?- Show all key bindingsCtrl-b :- Enter command mode
Practical Workflow Example
Let's walk through a typical session for working on a Node.js API:
# Start a named session
tmux new -s api-work
# Create a window for the dev server
cd ~/projects/api
npm run dev
# Ctrl-b c (create new window)
# Second window for running tests
cd ~/projects/api
npm test -- --watch
# Ctrl-b c (create new window)
# Third window for git operations and general shell work
cd ~/projects/api
# Ctrl-b " (split horizontally)
# Bottom pane for logs
tail -f logs/development.log
# Rename windows for clarity
# Ctrl-b , then type "server" and Enter
# Switch to window 1, Ctrl-b , then type "tests"
# Switch to window 2, Ctrl-b , then type "shell"
# Now you have a organized workspace
# Ctrl-b d to detach
# Close laptop, go home, open laptop
tmux attach -t api-work
# Everything is exactly as you left it
Configuration: .tmux.conf Essentials
Out of the box, tmux's defaults are functional but austere. A minimal configuration file dramatically improves usability.
Create ~/.tmux.conf:
# Use Ctrl-a as prefix instead of Ctrl-b (easier to reach)
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix
# Enable mouse support (click to switch panes, resize, scroll)
set -g mouse on
# Start window and pane numbering at 1 instead of 0
set -g base-index 1
setw -g pane-base-index 1
# Increase scrollback buffer from default 2000 to 50000 lines
set-option -g history-limit 50000
# Reload config file with Ctrl-a r
bind r source-file ~/.tmux.conf \; display "Config reloaded!"
# Better split pane shortcuts (| for vertical, - for horizontal)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Vim-style pane navigation
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# Status bar customization
set -g status-style 'bg=#333333 fg=#5eacd3'
set -g status-left-length 40
set -g status-left '#[fg=#ffffff,bg=#5eacd3,bold] #S #[default] '
set -g status-right '#[fg=#ffffff,bg=#666666] %H:%M %d-%b-%y '
This configuration:
- Changes prefix to
Ctrl-a(less finger gymnastics) - Enables mouse support (click panes, drag borders to resize, scroll output)
- Uses sensible numbering starting at 1
- Increases scrollback for reading long output
- Adds intuitive split commands (
Ctrl-a |andCtrl-a -) - Provides Vim-style pane navigation (
Ctrl-a h/j/k/l) - Improves status bar visibility
After saving, reload with:
tmux source-file ~/.tmux.conf
Or from inside tmux: Ctrl-a : then type source-file ~/.tmux.conf
This 15-line config makes tmux immediately more comfortable. You can expand it over time, but this foundation is enough for productive work.
Advanced: Named Sessions for Project Organization
A powerful pattern is creating dedicated sessions for each project:
# Start a new project
tmux new -s frontend
cd ~/projects/frontend-app
npm run dev
# Switch contexts without killing the dev server
# Ctrl-a d
# Start working on backend
tmux new -s backend
cd ~/projects/api
./run-local.sh
# See all projects
tmux ls
# frontend: 1 window
# backend: 1 window
# Jump between them
tmux attach -t frontend
# Work on frontend
# Ctrl-a d
tmux attach -t backend
# Work on backend
# When done for the day, just detach
# Everything keeps running
# Tomorrow: tmux attach -t frontend
This approach gives you instant context switching without the overhead of starting and stopping services.
zellij: The Modern Alternative
zellij is a newer terminal multiplexer written in Rust, designed to fix some of tmux's rough edges. It launched in 2021 and has quickly gained traction for its beginner-friendly defaults, built-in layout system, and modern architecture.
What Makes zellij Different
Better defaults: zellij works well out of the box. No configuration required for mouse support, sensible key bindings, or a readable status bar.
Mode-based keybindings: Instead of memorizing dozens of prefix combinations, zellij uses modes (like Vim). Press Ctrl-o for session mode, Ctrl-p for pane mode, etc. The status bar always shows available commands.
Built-in layout system: Define terminal layouts in KDL (a friendly configuration language) and load them with one command. No scripting required.
Rust foundation: Fast, memory-safe, and modern. Excellent performance and reliability.
Better discoverability: The UI actively helps you learn. The status bar shows what you can do in each mode.
Installing zellij
On macOS:
brew install zellij
On Linux with cargo:
cargo install --locked zellij
Or download pre-built binaries from GitHub releases.
Verify installation:
zellij --version
Basic Usage
Start a new session:
zellij
You'll see a clean interface with a helpful status bar at the bottom showing available modes.
The mode system is zellij's defining feature. Press:
Ctrl-o- Session mode (create, switch, detach)Ctrl-p- Pane mode (split, close, navigate)Ctrl-t- Tab mode (create, switch, rename)Ctrl-n- Resize mode (resize panes)Ctrl-s- Scroll mode (scroll through output)Ctrl-g- Lock mode (lock interface)
When you enter a mode, the status bar shows what each key does. For example, in pane mode (Ctrl-p):
n- Create new panex- Close current panef- Toggle fullscreenArrow keys- Navigate panes
This discoverability makes zellij much easier to learn than tmux.
Session Management
Create a named session:
zellij -s my-project
Detach from a session:
# Press: Ctrl-o, then d
List sessions:
zellij list-sessions
Attach to a session:
zellij attach my-project
Delete a session:
zellij delete-session my-project
The Layout System
zellij's killer feature is its layout system. You can define reusable terminal layouts in KDL files.
Example layout for a full-stack project at ~/.config/zellij/layouts/fullstack.kdl:
layout {
pane size=1 borderless=true {
plugin location="zellij:tab-bar"
}
pane split_direction="vertical" {
pane split_direction="horizontal" {
pane command="npm" {
args "run" "dev"
cwd "/home/user/projects/frontend"
name "Frontend"
}
pane command="npm" {
args "run" "dev"
cwd "/home/user/projects/backend"
name "Backend"
}
}
pane split_direction="horizontal" {
pane name="Tests"
pane command="tail" {
args "-f" "logs/app.log"
name "Logs"
}
}
}
pane size=2 borderless=true {
plugin location="zellij:status-bar"
}
}
Load this layout:
zellij --layout fullstack
Your terminal opens with:
- Top left: frontend dev server running
- Top right: backend dev server running
- Bottom left: empty pane for running tests
- Bottom right: tail following your logs
Everything starts automatically. This is incredibly powerful for consistent project setup.
A simpler layout for general development at ~/.config/zellij/layouts/dev.kdl:
layout {
pane
pane split_direction="vertical" {
pane
pane
}
}
This gives you three panes: one large on the left (for your main work), two stacked on the right (for servers, logs, or tests).
Key Bindings Reference
Unlike tmux's uniform prefix system, zellij uses mode-specific bindings:
Session Mode (Ctrl-o):
d- Detachw- Session manager (switch/create sessions)
Pane Mode (Ctrl-p):
n- New pane (splits based on available space)x- Close panef- Toggle fullscreenArrow keys- Navigate panesr- Rename pane
Tab Mode (Ctrl-t):
n- New tabx- Close tabr- Rename tabTab- Switch to next tab1-9- Switch to tab by number
Resize Mode (Ctrl-n):
Arrow keys- Resize active pane=- Reset pane sizes+- Increase size-- Decrease size
Scroll Mode (Ctrl-s):
Arrow keysorj/k- Scroll up/downCtrl-f/b- Page down/ups- Search in scrollback
Press Esc to exit any mode.
Configuration
While zellij works great with defaults, you can customize it via ~/.config/zellij/config.kdl:
// Simplified mode enables common features without mode switching
simplified_ui true
// Default shell
default_shell "zsh"
// Mouse support (enabled by default)
mouse_mode true
// Copy on select (automatically copy text when selecting with mouse)
copy_on_select true
// Theme
theme "gruvbox-dark"
// Default layout to use when starting without --layout flag
default_layout "compact"
zellij includes several built-in themes and layouts. List them:
zellij setup --dump-layout default
zellij setup --dump-layout compact
The beauty of zellij is that this configuration is optional. The defaults are already solid.
tmux vs zellij: Choosing the Right Tool
Both tools solve the same core problem but with different philosophies.
Choose tmux if:
- You need maximum stability and maturity (tmux has been battle-tested for 15+ years)
- You work on servers where tmux is already installed (it's ubiquitous in production environments)
- You want extensive customization and plugin ecosystem
- You're comfortable with traditional Unix tool design philosophy
- You need compatibility with existing tmux scripts and workflows
Choose zellij if:
- You want something that works great out of the box with minimal configuration
- You prefer modern UX with discoverability and helpful UI
- You plan to use the layout system for consistent project setups
- You're starting fresh and don't have existing tmux muscle memory
- You value Rust's performance and memory safety
- You like mode-based interfaces (similar to Vim)
The honest comparison:
| Feature | tmux | zellij |
|---|---|---|
| Learning curve | Steep | Gentle |
| Default experience | Needs config | Great out of box |
| Maturity | 15+ years | 3 years |
| Server availability | Nearly universal | Rarely pre-installed |
| Layouts | Manual scripting | Built-in KDL system |
| Performance | Excellent | Excellent |
| Customization | Extensive | Growing |
| Community | Massive | Growing |
For personal development on your own machine, zellij's better defaults and layout system are compelling. For work on remote servers or in enterprise environments, tmux's ubiquity makes it the pragmatic choice.
Many developers learn both: use tmux on servers where it's already installed, and zellij locally for the better UX.
Practical Workflow: AI Coding Agent Sessions
Let's walk through a real scenario: using Claude Code for a large refactoring that might take hours.
Problem
You're refactoring a large TypeScript codebase. Claude Code needs to analyze hundreds of files, suggest changes, and make modifications. This could take 2-3 hours. You need to:
- Keep the session alive if your laptop sleeps
- Maintain the session across network disconnects
- Be able to detach and check back later
- Prevent losing progress if the terminal crashes
Solution with tmux
# Start a dedicated session
tmux new -s claude-refactor
# Navigate to project
cd ~/projects/large-app
# Start Claude Code
claude-code
# Tell Claude: "Refactor all API calls to use the new async/await pattern"
# Claude starts working...
# Split pane to monitor files changing
# Ctrl-a " (horizontal split)
watch -n 2 'git status --short'
# You can detach anytime
# Ctrl-a d
# Go do other work, close laptop, whatever
# Hours later, reattach:
tmux attach -t claude-refactor
# Claude's work is exactly where you left it
# Review changes in the watch pane
# Respond to any questions Claude asked
# When finished:
exit # Exit Claude Code
# Ctrl-a & to kill the window
tmux kill-session -t claude-refactor
Solution with zellij
# Start session with custom layout
zellij -s claude-refactor --layout ~/layouts/agent-work.kdl
Where ~/layouts/agent-work.kdl contains:
layout {
pane split_direction="horizontal" {
pane size="70%" {
name "Agent"
}
pane split_direction="vertical" {
pane {
name "Files"
command "watch"
args "-n" "2" "git status --short"
}
pane {
name "Logs"
command "tail"
args "-f" "logs/claude-code.log"
}
}
}
}
This layout automatically gives you:
- Large left pane for Claude Code
- Upper right pane watching file changes
- Lower right pane tailing logs
# In the Agent pane, start Claude Code
claude-code
# Layout is already set up for monitoring
# Detach: Ctrl-o, then d
# Later:
zellij attach claude-refactor
# Everything persists perfectly
Another Example: Remote Dev Server
You're running a Next.js development server on a remote machine via SSH:
With tmux:
# SSH into remote server
ssh dev-server
# Start tmux session
tmux new -s nextjs-dev
# Start the dev server
cd ~/projects/webapp
npm run dev
# Detach: Ctrl-b d
# Exit SSH: exit
# Server keeps running
# Later, reconnect:
ssh dev-server
tmux attach -t nextjs-dev
# Dev server still running
# All logs preserved
With zellij:
ssh dev-server
zellij -s nextjs-dev
cd ~/projects/webapp
npm run dev
# Ctrl-o, d to detach
# exit to close SSH
# Later:
ssh dev-server
zellij attach nextjs-dev
# Still running
The pattern is identical: the multiplexer keeps your session alive independent of your SSH connection.
Terminal Sessions in Agents UI
For developers working exclusively on macOS who want session persistence without manual setup, Agents UI bundles zellij automatically. When you create a new terminal session in Agents UI, zellij runs transparently in the background, providing automatic persistence.
This means:
- Close the Agents UI window: sessions persist
- Computer goes to sleep: sessions persist
- Agents UI crashes: sessions persist
- Restart your computer: sessions persist (until you explicitly kill them)
You get all the benefits of zellij without thinking about it. The UI handles session creation, naming, and attachment. You just focus on your work.
For developers who need to work on remote servers or prefer configurability, learning tmux or zellij directly is still valuable. But for local AI agent workflows on macOS, Agents UI eliminates the setup overhead.
Getting Started Today
If you're new to persistent sessions, start with this path:
Install both tools to try them:
brew install tmux zellijStart with zellij for local work because the defaults are better:
zellij # Start your dev server # Ctrl-o, d to detach # zellij attach to reconnectLearn tmux basics for remote servers:
ssh your-server tmux new -s work # Do your work # Ctrl-b d to detachCreate a simple .tmux.conf if you choose tmux (use the 15-line config from earlier)
Try a zellij layout for a project you work on regularly
Within a week, session persistence will feel natural. You'll wonder how you ever worked without it.
Conclusion
Persistent terminal sessions aren't just a nice-to-have feature—they're fundamental infrastructure for modern development. When you're running AI coding agents that operate over extended periods, working on remote servers, or managing long-running processes, the ability to detach and reattach to sessions becomes essential.
tmux offers maturity, ubiquity, and extensive customization. It's the industry standard for good reason, and it's likely already installed on any server you SSH into. With a simple configuration file, it becomes a powerful workspace manager.
zellij offers a modern take on the same problem with better defaults, more discoverability, and a built-in layout system that makes complex setups trivial. It's the better choice for local development where you control the environment.
Both tools are excellent. Learn the basics of both, then choose based on your context: tmux for remote servers and established workflows, zellij for local development and better UX.
Either way, once you experience the freedom of detaching from a session, closing your laptop, and reattaching hours later to find everything exactly as you left it, you'll never go back to ephemeral sessions.
Agents UI bundles zellij for automatic session persistence on macOS, eliminating setup overhead for AI coding agent workflows. The terminal is open source and designed specifically for running multiple AI agents simultaneously. Learn more about Agents UI.