Persistent Terminal Sessions: A Practical Guide to tmux and zellij

Learn how to keep terminal sessions alive across disconnects and reboots using tmux and zellij — with setup guides, key bindings, and workflow examples.

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:

  1. A multiplexer server process starts
  2. This server creates and manages persistent sessions
  3. Your terminal window attaches as a client to view the session
  4. When you disconnect, the server keeps running with all your processes intact
  5. 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 session
  • Ctrl-b s - List and switch sessions
  • Ctrl-b $ - Rename current session

Windows:

  • Ctrl-b c - Create new window
  • Ctrl-b n - Next window
  • Ctrl-b p - Previous window
  • Ctrl-b 0-9 - Switch to window by number
  • Ctrl-b , - Rename current window
  • Ctrl-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 panes
  • Ctrl-b x - Kill current pane
  • Ctrl-b z - Zoom current pane (fullscreen toggle)
  • Ctrl-b { - Move pane left
  • Ctrl-b } - Move pane right

Other:

  • Ctrl-b ? - Show all key bindings
  • Ctrl-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 | and Ctrl-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 pane
  • x - Close current pane
  • f - Toggle fullscreen
  • Arrow 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 - Detach
  • w - Session manager (switch/create sessions)

Pane Mode (Ctrl-p):

  • n - New pane (splits based on available space)
  • x - Close pane
  • f - Toggle fullscreen
  • Arrow keys - Navigate panes
  • r - Rename pane

Tab Mode (Ctrl-t):

  • n - New tab
  • x - Close tab
  • r - Rename tab
  • Tab - Switch to next tab
  • 1-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 keys or j/k - Scroll up/down
  • Ctrl-f/b - Page down/up
  • s - 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:

  1. Install both tools to try them:

    brew install tmux zellij
    
  2. Start with zellij for local work because the defaults are better:

    zellij
    # Start your dev server
    # Ctrl-o, d to detach
    # zellij attach to reconnect
    
  3. Learn tmux basics for remote servers:

    ssh your-server
    tmux new -s work
    # Do your work
    # Ctrl-b d to detach
    
  4. Create a simple .tmux.conf if you choose tmux (use the 15-line config from earlier)

  5. 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.

Try Agents UI

A native terminal for AI coding agents with persistent sessions, SSH workflows, and built-in editing.