# A deep dive into tmux

> Learn tmux from scratch: create persistent sessions, use windows and panes, copy text, customize key bindings, and work safely over SSH.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-09-09 | Topics: [CLI](https://flaviocopes.com/tags/cli/) | Canonical: https://flaviocopes.com/tmux/

tmux lets you run several terminal programs inside one terminal window. You can split the screen, open more shells, and jump between them with the keyboard.

But the feature that matters most is persistence. A tmux session keeps running when you close the terminal or lose an SSH connection. You reconnect later and everything is still there, where you left it.

On a laptop this is nice to have. On a remote server it changes how you work.

In this tutorial we start from zero. We install tmux, learn how it thinks about sessions and windows, build a small workspace, tweak the configuration, and use it over SSH.

## What is tmux?

tmux is a **terminal multiplexer**. The name sounds more complicated than the idea.

A normal terminal window shows one shell. With tmux, that same window can show many shells and programs.

There is a second part, and it's the one that makes tmux special. tmux separates the programs from the terminal application that displays them.

When you start tmux, it creates a background server. That server owns your sessions and the programs running in them. The terminal you look at is just a client connected to that server.

```text
Terminal app → tmux client → tmux server → shells and programs
```

You can disconnect the client, and the server keeps going. tmux calls this **detaching**. When you connect again, you **attach** to the same session.

tmux runs inside the terminal you already have. Terminal.app, iTerm2, Ghostty, Kitty, Alacritty, they all work. It does not replace your shell either. Zsh, Bash, Fish, whatever you use today keeps working.

## Why use tmux?

The first reason is the one I already mentioned. Work survives.

Say you SSH into a server and start a long database import. Halfway through, your Wi-Fi drops. Without tmux, the remote shell can close and take the import down with it.

Inside tmux, the session keeps running on the server. You reconnect, attach, and keep watching the import.

Then there is organization. One project might need an editor, a dev server, a test watcher, and a plain shell. tmux gives each one its own window or pane, all inside one session.

And tmux is scriptable. It has a command language, so you can create sessions, windows, and panes from a shell script. One command rebuilds the same workspace every morning.

tmux is not always the answer, though.

If you run short commands in one terminal, your terminal's tabs are probably enough. And if a server program must start after a reboot and run without you, use a service manager like systemd. tmux keeps an interactive session alive. It is not a process supervisor for production.

## Install tmux

On macOS, install tmux with Homebrew:

```bash
brew install tmux
```

On Omarchy, use the Omarchy package command:

```bash
omarchy pkg add tmux
```

On Ubuntu or Debian:

```bash
sudo apt update
sudo apt install tmux
```

On Fedora:

```bash
sudo dnf install tmux
```

On Arch Linux:

```bash
sudo pacman -S tmux
```

Check the installed version:

```bash
tmux -V
```

You should see something like:

```text
tmux 3.7c
```

Your version may differ. The tmux project supports its latest release, and operating system repositories often ship an older one.

## Start your first tmux session

Run:

```bash
tmux
```

Not much changes. A status line appears at the bottom of the terminal. That's it.

Behind that small change, tmux created a server, a session named `0`, a window named after your shell, one pane filling that window, and a client attached to the session.

Run a normal command:

```bash
pwd
```

Nothing special. Programs inside tmux behave like programs in any terminal.

Now start something we can recognize later:

```bash
top
```

This shows a live view of the running processes. Press `q` when you want to close it. Leave it running for now.

![The top process monitor running inside tmux](https://flaviocopes.com/images/tmux/top-started.png)

The values keep updating while `top` runs:

![The top process list updating inside tmux](https://flaviocopes.com/images/tmux/top-updating.png)

## Understand the prefix key

Almost every tmux shortcut starts with a **prefix key**. The default is `Ctrl-b`.

You hold `Ctrl`, press `b`, release both. Then you press the command key.

To detach, press:

```text
Ctrl-b, then d
```

Don't press all three keys together. Prefix first, release, then `d`.

You're back in your normal shell, and tmux prints:

```text
[detached (from session 0)]
```

![A tmux session detached in the terminal](https://flaviocopes.com/images/tmux/detached-session.png)

`top` is still running inside tmux. Attach again:

```bash
tmux attach
```

The process list is still updating:

![The same top process monitor after attaching to tmux again](https://flaviocopes.com/images/tmux/attached-session.png)

This is the first tmux habit to build. From now on, when I write `Ctrl-b c`, I mean `Ctrl-b`, release, then `c`.

## Sessions, windows, and panes

tmux organizes everything in three levels:

```text
Server
└── Session
    ├── Window
    │   ├── Pane
    │   └── Pane
    └── Window
        └── Pane
```

A **session** is a whole workspace. I might have one session for `flaviocopes.com` and another for server maintenance.

A **window** fills the tmux client. Think of a terminal tab. One window has my editor, another has logs.

A **pane** is a split inside a window. One pane runs the dev server, another runs the tests.

If you want a shortcut: the session is the project, the window is the task, the pane is one terminal area. The names stop feeling strange once you use them.

## Create a named session

Session `0` works, but a name is easier to remember.

Detach from the current session:

```text
Ctrl-b d
```

Create a session named `website`:

```bash
tmux new -s website
```

`new` is short for `new-session`. `-s` sets the session name. The full command works too:

```bash
tmux new-session -s website
```

From a normal shell, list every session:

```bash
tmux list-sessions
```

Or the short version:

```bash
tmux ls
```

You get something like:

```text
0: 1 windows (created Mon Aug 31 17:10:00 2026)
website: 1 windows (created Mon Aug 31 17:12:00 2026) (attached)
```

Detach from `website`, then attach to it by name:

```bash
tmux attach -t website
```

`-t` means **target**. Many tmux commands take one. It tells tmux which session, window, or pane the command is for.

## Rename a session

Inside tmux, press:

```text
Ctrl-b $
```

Type the new name, press `Enter`.

From the shell:

```bash
tmux rename-session -t website blog
```

Names matter once you have several sessions open. I use project names or server roles. Never `work2`.

## Create and move between windows

Create a new window:

```text
Ctrl-b c
```

The status line now lists two windows.

Next window:

```text
Ctrl-b n
```

Previous window:

```text
Ctrl-b p
```

Or pick one by number:

```text
Ctrl-b 0
Ctrl-b 1
```

Window numbers start at `0` by default.

For an interactive list of sessions and windows:

```text
Ctrl-b w
```

Move with the arrow keys and press `Enter`.

Rename the current window:

```text
Ctrl-b ,
```

I use short task names: `editor`, `server`, `tests`, `logs`.

tmux may rename a window on its own when the program in the foreground changes. We'll turn that off later in the configuration, if you prefer stable names.

To close a window, exit every shell inside it:

```bash
exit
```

Or tell tmux to kill it:

```text
Ctrl-b &
```

tmux asks for confirmation, because this stops every program in that window.

## Split a window into panes

Split the current window left and right:

```text
Ctrl-b %
```

![A tmux window split into left and right panes](https://flaviocopes.com/images/tmux/two-panes.png)

Split the current pane top and bottom:

```text
Ctrl-b "
```

![A tmux window with three panes](https://flaviocopes.com/images/tmux/three-panes.png)

The quote key is hard to remember. We'll add friendlier split keys in the configuration section.

Move between panes with the prefix and an arrow key:

```text
Ctrl-b Arrow key
```

So `Ctrl-b`, release, then the left arrow.

![The bottom-right tmux pane selected](https://flaviocopes.com/images/tmux/select-bottom-pane.png)

Prefix and left arrow again selects the big pane:

![The left tmux pane selected](https://flaviocopes.com/images/tmux/select-left-pane.png)

To see the pane numbers:

```text
Ctrl-b q
```

tmux draws a big number on every pane. Press that number quickly to jump to it.

Next pane:

```text
Ctrl-b o
```

Close a pane by exiting its shell:

```bash
exit
```

Or kill it:

```text
Ctrl-b x
```

Again, tmux asks first.

## Resize and arrange panes

Resize the current pane with:

```text
Ctrl-b Ctrl-Arrow
```

Each press resizes by a little, and you may need the prefix again every time.

tmux also ships a few ready-made layouts. Press:

```text
Ctrl-b Space
```

Each press cycles to the next layout: even rows, even columns, a large main pane, a tiled grid.

Zoom the current pane:

```text
Ctrl-b z
```

The pane fills the whole window. Press the same shortcut again and the layout comes back.

Zoom helps when a small pane has long output, or when you want the editor to take the whole screen for a while.

## A small development workspace

Let's put this together into a real layout.

From a normal shell, create a fresh named session:

```bash
tmux new -s notes
```

Rename the first window:

```text
Ctrl-b ,
```

Call it `code`.

Split it left and right:

```text
Ctrl-b %
```

The left pane is for the editor:

```bash
nvim .
```

Move right with `Ctrl-b Right` and start the dev server:

```bash
npm run dev
```

Create a second window with `Ctrl-b c` and rename it `shell`.

One session, two tasks:

```text
notes
├── code
│   ├── editor pane
│   └── development server pane
└── shell
    └── normal shell pane
```

Detach with `Ctrl-b d`. Editor and server keep running.

Come back later with:

```bash
tmux attach -t notes
```

## Use tmux command mode

Every shortcut you pressed so far runs a tmux command under the hood. You can run those commands yourself. Press:

```text
Ctrl-b :
```

A prompt appears in the status line. Type:

```text
rename-window server
```

and press `Enter`.

This is **command mode**. It helps when you remember the command but not the key. It's also a good way to try a configuration change before saving it to a file.

For example, turn on mouse support for the current server:

```text
set -g mouse on
```

`-g` sets the global value.

Now you can click a pane to select it, click windows in the status line, drag pane borders to resize, and scroll with the wheel.

The setting is gone when the tmux server stops. To keep it, put it in the configuration file. We get there in a moment.

## Scroll through old output

Your terminal's normal scrollback does not always work as expected inside tmux. tmux keeps its own history for each pane.

To read it, enter copy mode:

```text
Ctrl-b [
```

Now the arrow keys, `Page Up`, and `Page Down` move through the pane history. Press `q` to get out.

`Ctrl-b Page Up` also enters copy mode.

The `history-limit` option controls how many lines tmux saves. More lines means more memory, but long build logs become much easier to inspect.

## Copy text

Copy mode also selects and copies text, into tmux's own paste buffer.

The selection keys depend on whether copy mode uses Emacs-style or Vim-style keys. I prefer Vim-style. Set it in command mode:

```text
set -g mode-keys vi
```

Enter copy mode:

```text
Ctrl-b [
```

Move to where the text starts. Press `Space` to start selecting. Move to the end and press `Enter`.

Paste with:

```text
Ctrl-b ]
```

Note that this copies into the tmux buffer, not necessarily into the system clipboard.

Clipboard integration depends on your terminal, your tmux version, the operating system, and whether you are over SSH. Start by letting tmux use the terminal's clipboard support:

```text
set -g set-clipboard on
```

Modern terminals accept clipboard data through an escape sequence. Some need their own permission or setting before they allow it.

One word of caution on remote servers. A clipboard escape sequence sent by a remote program can change your local clipboard. Use terminals and servers you trust.

## Search pane history

Copy mode can search too.

With Vim-style keys, enter copy mode and press `/`:

```text
Ctrl-b [
/
```

Type what you're looking for and press `Enter`. `n` jumps to the next match, `N` to the previous one.

Much faster than rerunning a command when the error you need is still in the history.

## Send the prefix to a nested program

Sometimes a program inside tmux also wants `Ctrl-b`.

Press the prefix twice:

```text
Ctrl-b Ctrl-b
```

The first one talks to tmux. The second one reaches the program in the pane.

The same applies when you run tmux inside tmux.

Nested tmux gets confusing fast. Both layers have sessions, windows, panes, a status line, and a prefix key. I avoid it unless the two layers have clear, separate jobs.

The one reasonable case is a local tmux connecting to a remote machine that already runs tmux. There, I let the remote tmux own the persistent work and keep the local layer minimal.

## Manage sessions from the shell

You don't have to attach to manage tmux.

Create a detached session:

```bash
tmux new-session -d -s reports
```

`-d` starts it detached.

Add a window to it:

```bash
tmux new-window -t reports -n logs
```

Type a command into that window:

```bash
tmux send-keys -t reports:logs 'tail -f /var/log/nginx/access.log' Enter
```

Attach when you're ready:

```bash
tmux attach -t reports
```

Targets have this shape:

```text
session:window.pane
```

For example:

```text
reports:logs.0
```

Names work in most places where numbers do, and they make scripts much easier to read.

Kill one session:

```bash
tmux kill-session -t reports
```

Kill every session except `website`:

```bash
tmux kill-session -a -t website
```

Stop the whole tmux server:

```bash
tmux kill-server
```

That ends every session and every process inside them. Think before you run it.

## Configure tmux

tmux reads your configuration from:

```text
~/.tmux.conf
```

It also accepts the XDG path:

```text
~/.config/tmux/tmux.conf
```

Pick one and stick with it, so you always know which file you're editing.

Here is a small configuration I would start with:

```bash
set -g mouse on
set -g history-limit 50000
set -g mode-keys vi
set -g renumber-windows on
set -g set-clipboard on

set -g base-index 1
setw -g pane-base-index 1

set -g allow-rename off

bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

bind r source-file ~/.tmux.conf \; display-message "Config reloaded"
```

Let's go through it.

`mouse on` lets you click and resize panes.

`history-limit` keeps 50,000 lines per pane.

`mode-keys vi` gives you Vim-style copy mode.

`renumber-windows` closes the gaps in the numbering when a window goes away.

`base-index` and `pane-base-index` start numbering at `1`. That matches the leftmost number key on the keyboard, which feels more natural to me.

`allow-rename off` stops programs from renaming your windows.

The two `bind` lines give you splits that are easier to remember:

```text
Ctrl-b |
Ctrl-b -
```

The `-c "#{pane_current_path}"` part opens the new pane in the same directory as the current one.

The last line reloads the configuration with:

```text
Ctrl-b r
```

If you use the XDG path, point the reload command at that file instead.

## Reload the configuration

A running tmux server does not reread the configuration file when you change it.

Reload it from your shell:

```bash
tmux source-file ~/.tmux.conf
```

Or press `Ctrl-b r` if you added the binding above.

If something is wrong, tmux tells you the line number. Fix that line and reload again.

You don't need to restart your sessions. Most option and key changes apply right away.

## See every key binding

Press:

```text
Ctrl-b ?
```

tmux opens a searchable list of the current bindings. `q` closes it.

From a shell:

```bash
tmux list-keys
```

This shows the configuration that is actually active, including your changes.

For options:

```bash
tmux show-options -g
```

Window options:

```bash
tmux show-window-options -g
```

These are the first things to check when a configuration you copied from somewhere behaves differently than expected.

## Change the prefix key

Many people move the prefix from `Ctrl-b` to `Ctrl-a`.

Add:

```bash
unbind C-b
set -g prefix C-a
bind C-a send-prefix
```

Reload. The prefix is now `Ctrl-a`.

The last line lets you send a real `Ctrl-a` to a program by pressing it twice.

My advice is to keep `Ctrl-b` at first. Tutorials and server configurations use the default, and you'll be able to follow them. Change it only when the default gets in your way for real.

## Navigate panes without the prefix

You can bind keys in the root table. Those work without the prefix.

For example:

```bash
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
```

`M` is the Meta key, usually `Alt` or `Option`. Now `Alt-Arrow` moves between panes.

Your terminal or your operating system may already use those shortcuts. Test each one before you rely on it.

Prefix-less shortcuts are convenient, but every key you take is a key the programs inside tmux can no longer see. I keep them to a minimum.

## Build a session with a shell script

tmux commands are normal shell commands. That makes repeatable workspaces easy.

Create `dev-session.sh`:

```bash
#!/usr/bin/env bash

session="website"
project="$HOME/www/flaviocopes.com"

if ! tmux has-session -t "$session" 2>/dev/null; then
  tmux new-session -d -s "$session" -c "$project" -n editor
  tmux send-keys -t "$session:editor" 'nvim .' Enter

  tmux new-window -t "$session" -c "$project" -n server
  tmux send-keys -t "$session:server" 'npm run dev' Enter

  tmux new-window -t "$session" -c "$project" -n shell
fi

tmux attach-session -t "$session"
```

Make it executable:

```bash
chmod +x dev-session.sh
```

Run it:

```bash
./dev-session.sh
```

The first run creates the workspace. Later runs find the existing session and attach to it.

Notice the quotes around the variables. A project path can contain spaces.

Also notice how `send-keys` works. It sends text, then an `Enter` key, exactly as if you typed it. It has no idea whether the target pane is sitting at a shell prompt or in the middle of something else.

Right after creating a fresh shell, that's safe. Against an old pane in an unknown state, it's a gamble.

## Capture pane output

Scripts can read text out of a pane:

```bash
tmux capture-pane -p -t website:server
```

`-p` prints the captured text to standard output.

Only the last 100 lines:

```bash
tmux capture-pane -p -S -100 -t website:server
```

Handy for diagnostics and small automation scripts.

What you get is text, not structured process state. Your script still has to make sense of it.

If you need a reliable health check, ask the service itself. Hit an HTTP endpoint, check the process status, read an exit code. Don't guess from what happens to be visible on screen.

## Use tmux over SSH

Remote work is where tmux earns its place.

Connect to the server:

```bash
ssh notes-server
```

Start or attach to a named session:

```bash
tmux new -A -s work
```

`-A` means attach if the session already exists. So one command covers both cases. If `work` exists you land in it, if not tmux creates it.

Now start your editor, your logs, or that maintenance command.

When the SSH connection drops, reconnect:

```bash
ssh notes-server
tmux new -A -s work
```

Everything is still there.

If remote access is new to you, my [SSH for developers](https://flaviocopes.com/ssh-for-developers/) guide covers keys, host verification, configuration aliases, tunnels, and file transfers.

## What survives and what does not

tmux keeps a session alive for as long as the tmux server process is alive. That's the whole rule.

So closing the terminal application, detaching, losing the SSH connection, or putting your laptop to sleep while tmux runs on a remote machine: all fine.

Rebooting the machine that runs the tmux server, killing the server, or a system crash: the sessions are gone. And tmux can't save a program that crashes inside a pane.

tmux is not a process checkpoint. It does not save a program's memory and bring it back after a reboot.

Plugins can save layouts and the commands that were running, and restart them when tmux comes back. That is useful, but it's a fresh start, not a live process resuming from the exact instruction where it stopped.

For workloads that matter on a server, use systemd, containers with a restart policy, or another supervisor. Use tmux to look at them and poke at them.

## Use the same session from two terminals

Several clients can attach to one session.

Open two terminal windows and run this in both:

```bash
tmux attach -t website
```

Both show the same session. What you do in one appears in the other.

This works for pair programming, or for watching the same remote session from two devices.

There is one catch. A shared window has to fit every attached client, so tmux may size it for the smallest one. The bigger client ends up with unused space.

If you want to share the windows but pick a different current window in each client, tmux can create a linked session:

```bash
tmux new-session -t website -s website-mobile
```

`website-mobile` shares its windows with `website`, but each session has its own current window.

They're still the same live windows underneath. Close one in either session and it's gone from both.

## Understand sockets and separate servers

tmux clients talk to the server through a Unix socket.

Most of the time you never think about it. Running `tmux` finds the default socket and connects to the server behind it.

You can use a separate named socket:

```bash
tmux -L experiments new -s test
```

List the sessions on that socket:

```bash
tmux -L experiments ls
```

This is a completely separate tmux server. Its sessions don't show up in a plain `tmux ls`.

Named sockets are good for testing a new configuration, or for isolating automation from your real sessions.

Be careful with where sockets live. Don't put them in shared, writable directories unless you understand the permissions. Whoever controls your tmux server can read your pane output and type into your terminals.

## Environment variables inside long-lived sessions

A tmux server can live for days. Your environment changes in the meantime.

When a new client attaches, tmux updates a small, configured set of environment variables. It does not touch the environment of shells that are already running.

This bites you after changing `PATH`, credentials, the SSH agent socket, or display settings. The old shell still has the old values.

Open a new shell, or restart the program that needs the fresh values.

To see what tmux itself knows:

```bash
tmux show-environment
```

And don't put long-lived secrets in the tmux configuration or in session scripts. Depending on permissions, other local processes can read command lines, files, and pane history.

## Use plugins carefully

tmux is complete without plugins.

If you want them, the usual plugin manager is TPM, the tmux plugin manager. Popular plugins add session saving and restore, better copy behavior, themes, and more navigation keys.

Remember that a plugin is code running with your user permissions. Before installing one, read the source and the install instructions, check who maintains it, and understand which commands it runs. On a sensitive machine, pin the version or review each update.

And keep the configuration small enough that you can still debug it.

I'd learn plain tmux before adopting a big configuration framework. Otherwise you never know if a key comes from tmux, from a plugin, or from someone else's dotfiles.

## Common problems

### `no server running`

You ran something like:

```bash
tmux ls
```

and there is no tmux server yet. Start one:

```bash
tmux new -s work
```

### `sessions should be nested with care`

You're already inside tmux. Check with:

```bash
printf '%s\n' "$TMUX"
```

Empty means you're outside tmux. A path followed by numbers means you're inside.

Detach, or switch session from inside tmux:

```text
Ctrl-b s
```

Only unset `TMUX` if you really want a nested server and you understand the two layers.

### Colors look wrong

Check `TERM` outside tmux first:

```bash
printf '%s\n' "$TERM"
```

Inside tmux it's normally a tmux-specific value like:

```text
tmux-256color
```

Don't paste random `TERM` exports into your shell startup file. `TERM` describes the current terminal, and the terminal application and tmux are the ones that should set it.

Also make sure the remote machine has the matching terminfo entry. Older servers may not know `tmux-256color`.

### A shortcut does nothing

Show the active bindings:

```bash
tmux list-keys
```

Then check whether the terminal or the operating system grabs the key before tmux sees it.

For custom bindings, reload the right configuration file and read the error message, if there is one.

### Scrolling moves through shell history

Enter copy mode first:

```text
Ctrl-b [
```

Or turn on mouse mode in the configuration.

### A session disappeared

Check:

```bash
tmux ls
```

No server running means the machine rebooted or the server exited.

A session also ends when its last window closes. If a shell exits on its own, look at your shell startup files and at the command that launched it.

## How I would use tmux

Mostly on remote servers.

I would create one named session per job:

```bash
tmux new -A -s operations
```

One window for service status and logs. Another for a normal shell. Maybe a temporary pane for an editor or a one-off command.

The configuration would stay small. Mouse support, a bigger history, stable window names, easy splits, one reload key. That covers most of what I need.

Locally, I would use tmux when I want the same keyboard workflow on macOS and Linux. A session with `editor`, `server`, and `shell` windows is enough for most projects.

I would not use tmux to keep a production web server alive. systemd or the hosting platform should own that process. tmux is just where I'd go to look at it.

I would also stay away from automation that sends keystrokes into old panes and hopes they are ready. Shell scripts, service APIs, and health endpoints give you real guarantees.

If the problem were coordinating many coding agents, I would compare tmux with newer tools built around agent state. I wrote about that choice in [Herdr vs tmux vs Zellij](https://flaviocopes.com/herdr-vs-tmux-zellij/).

## A tmux shortcut cheat sheet

Every shortcut here starts with the default `Ctrl-b` prefix:

| Key | Action |
| --- | --- |
| `d` | Detach from the session |
| `c` | Create a window |
| `n` | Go to the next window |
| `p` | Go to the previous window |
| `0`–`9` | Select a window by number |
| `,` | Rename the current window |
| `w` | Choose a session or window |
| `%` | Split left and right |
| `"` | Split top and bottom |
| Arrow | Move to another pane |
| `q` | Show pane numbers |
| `o` | Move to the next pane |
| `z` | Zoom the current pane |
| `x` | Kill the current pane |
| `[` | Enter copy mode |
| `]` | Paste from the tmux buffer |
| `:` | Open command mode |
| `?` | Show key bindings |
| `s` | Choose a session |

Don't memorize all of it. Start with four things:

```text
Ctrl-b c    create a window
Ctrl-b %    split into panes
Ctrl-b d    detach
tmux attach attach again
```

Add the rest when you need it.

If you want the full picture, the official [tmux getting started guide](https://github.com/tmux/tmux/wiki/Getting-Started) and the [tmux manual](https://man7.org/linux/man-pages/man1/tmux.1.html) document every command and option. And if the command line itself still feels new, my free [Shell Commands course](https://flaviocopes.com/courses/terminal/) covers the basics that make tmux easier.
