
How-to10 min read
tmux: the viewfinder is not the engine
Linux · tmux · Shell · Terminal · Operations · SSH
tmux is a programmable session layer that decouples long-running work from the window you happen to be looking through. A how-to for junior engineers: four production situations, the expert mindset, and a complete script that builds a workspace without attaching too soon.
Begin with a distinction, not a feature list. tmux is a terminal multiplexer: a small server on the machine that owns sessions, windows, and panes, and a client that attaches your current terminal to that server. The window on your laptop is a viewfinder. The work lives in the engine.
If you have read Do you really understand the terminal?, you already know the injury that tmux exists to treat. Close a GUI emulator and the kernel sends SIGHUP down the PTY. Jobs die. tmux inserts its own PTY factory and keeps the session after hangup. That is not a convenience pane-splitter. That is a change of what is allowed to die.
This how-to is for a learner or junior engineer. We shall be rigorous: every scenario states a problem, a correct application, and a mistake that looks like competence. We end with a complete script—and we will correct two habits that tutorials still teach badly (chmod 777 on a socket; ~/ inside quoted strings).
What you will learn
- What
tmuxis (one sentence you can defend). - Four production-shaped uses: detach-safe work, one-screen workspace, shared incident session, scripted layout.
- How experts decide differently from beginners.
- A complete Bash script that builds a workspace detached, then attaches once.
One-sentence crystal
tmux is a headless session engine: processes keep a controlling TTY inside
tmuxafter your SSH client and GUI window are gone; you attach a disposable viewfinder when you wish to look, and you script the layout because you refuse to treat a living session as irreplaceable furniture.
What tmux is
| Phrase | Accurate? |
|---|---|
| “A way to split the terminal” | Incomplete. Splits are a view. The product is the server. |
| “A safety net if SSH drops” | A use. The mechanism is: your job’s TTY is a tmux pane, not the SSH PTY. |
| “A replacement for the shell” | No. Each pane runs a shell (or a program). tmux orchestrates them. |
| “A programmable session layer” | Yes. |
Three nouns, always:
| Noun | Meaning |
|---|---|
| Server | One process per user (typically), holds all sessions in memory. |
| Session | A named workspace (db-migration, api-backend). |
| Window / pane | A screenful, possibly split; each pane has a PTY and a foreground program. |
Client = tmux attach in this terminal. Close the client; the server remains.
Scenario 1 — Long work on the far side of an unreliable cable
The problem
You ssh to a host and start a four-hour migration, a large rsync, or an upgrade. Wi-Fi blinks. The laptop sleeps. The VPN dies. The SSH process dies. The remote shell is the session leader of that PTY. The kernel delivers SIGHUP. The job dies mid-write. Data may be inconsistent. “I will be careful” is not a control.
The correct application
Run the job inside a named session on the machine that owns the disks.
ssh admin@prod-db-01
# Named session: you can find it later without guessing
tmux new -s db-migration
python3 migrate_database.py --all
# Detach: prefix then d. Default prefix is Ctrl+b, then d.
# The GUI window may now close. The engine keeps the PTY.
Hours later, from this laptop or another:
ssh admin@prod-db-01
tmux attach -t db-migration
If you are unsure whether it exists:
tmux ls
How to apply it correctly
- Name the session after the operation, not after your mood (
db-migration, nottmp). - Start tmux before the dangerous command, not after you remember the lecture.
- Detach is optional for safety: even closing the SSH window should leave the
tmuxserver holding the pane. Confirm you are insidetmux(echo $TMUXis non-empty). - Logging: redirect the job as well (
tee migrate.log) so you are not solely dependent on scrollback.
Scenario 2 — One screen, many processes
The problem
A junior’s desktop becomes five GUI terminals: editor, tests, API logs, database logs. Focus dies. The arrangement cannot be rebuilt after a reboot.
The correct application
One client window. Several panes. Default prefix Ctrl+b:
| Keys after prefix | Effect |
|---|---|
% | Split vertically (left | right) |
" | Split horizontally (top / bottom) |
arrow keys / o | Move among panes |
c | New window (another full screen in the same session) |
n / p / 0–9 | Next / previous / numbered window |
d | Detach the client; engine stays |
This is a valid use. It is not yet expertise. Expertise is Scenario 4: you refuse to recreate the layout by hand the third time.
How to apply it correctly
- One window = one activity (edit vs watch logs), not one random split per impulse.
- Put long-running servers in panes that you will not casually
exit. - Learn to zoom a pane (
Ctrl+b z) instead of opening another GUI window.
Scenario 3 — Shared session for an incident (“war room”)
The problem
Two operators must see the same bytes and type into the same shell. Video sharing adds latency and treats the terminal as a picture. Pairing needs a shared PTY, not a shared rectangle of pixels.
The correct application
tmux can use an explicit socket (-S) so two logins attach to one server.
# Engineer A — create a session on a dedicated socket
tmux -S /tmp/incident-war-room new -s war-room
# Restrict the socket to a shared UNIX group (example: ops).
# Do not chmod 777 on a production host: any local user could attach and type.
chgrp ops /tmp/incident-war-room
chmod 660 /tmp/incident-war-room
# Engineer B
tmux -S /tmp/incident-war-room attach -t war-room
Both clients see the same panes. Either may type. That is the point and the risk.
How to apply it correctly
- Socket permissions are the security boundary.
chmod 777is a tutorial vice. Use a group and660, ortmux server-accesswhere your version supports it. - Announce who types. Two people in one shell is a shared stdin.
- When the incident ends: detach, kill-session if the socket was ad hoc, remove the socket file.
- This is not an audit log. If you need attribution, you need something else (shared
script, ticket, pair protocol).
Scenario 4 — The layout is code
The problem
Every morning: four splits, three services, twenty minutes of cd and Ctrl+b. The session is left up for forty days because recreating it is painful. That is fear of setup, not operational maturity.
The expert rule
If you have built the same pane layout three times, you script it.
You create the session detached (-d), send keys into named targets, then attach. If you omit -d, new-session attaches immediately and the rest of the script never runs.
Target grammar:
session:window.pane
example: my-project:infra.2
send-keys … C-m means: type this string, then Enter (carriage return).
Default pane index is 0 unless you set pane-base-index. The script below sets window and pane base index to 1 so infra.1 means the first pane—matching how humans count, and avoiding a class of off-by-one bugs.
Tilde in quotes: PROJECT_DIR="~/Projects/my-app" does not expand ~. Use "$HOME/Projects/my-app".
How experts think
Beginners use tmux as split screen plus SSH insurance. Experts use it as a programmable orchestrator that decouples computation from the glass.
1. Decouple the viewfinder from the engine
| Beginner | Expert |
|---|---|
The window is the server. Close it and the work should… well, tmux is a safety net. | The window is a disposable lens. The session is the source of truth. |
The expert SSHs in, attaches, starts or inspects, detaches, shuts the laptop. Connections are ephemeral. This is the same ontology as the PTY lecture: do not marry the job to the client.
2. Deterministic layout over precious furniture
| Beginner | Expert |
|---|---|
| Spends five minutes splitting; leaves the session for forty days | Assumes crash, reboot, accidental kill-pane; rebuilds in milliseconds |
Tools such as tmuxinator exist. A shell script is enough and is readable in an incident. The script below is that habit made concrete.
3. The buffer, not the picture
| Beginner | Expert |
|---|---|
| Mouse to click panes, scroll, copy | Hands on the home row; scrollback is a text buffer |
Copy-mode (often vi keys): prefix [, search with /, yank, prefix ]. The expert treats output as editable history, not as a screenshot. Configure this in ~/.tmux.conf when you are ready; do not postpone Scenario 1 until your config is pretty.
4. Sessions by context
| Beginner | Expert |
|---|---|
| One session, fifteen windows, “what was window 7?” | One session per mental context |
Example segregation:
| Session | Typical panes |
|---|---|
api-backend | editor, tests, git |
infra-deploy | Terraform, cloud CLI, bastion SSH |
log-monitoring | htop, journalctl, tail |
Jump with prefix s (session tree), or a fuzzy finder later. Windows are rooms in a house. Sessions are different houses.
Evolution matrix
| Paradigm | Novice | Expert |
|---|---|---|
| Primary use | Splits + survive SSH drop | Headless orchestration + automation |
| Navigation | Mouse, arrows, default prefix | Keys, later vi-copy and jumpers |
| Workspace | Ad hoc, precious | Declarative script |
| Copy | OS clipboard + mouse | tmux paste buffer (then OS clipboard if configured) |
Level 5 — A workspace script
Target layout
- Session:
my-project - Window
editor: one pane, editor - Window
infra: left ~50% app server; top-right ~25% container logs; bottom-right ~25% free shell
Save as setup_project.sh (for example in the project or ~/bin).
#!/usr/bin/env bash
# Build (once) and attach a deterministic tmux workspace.
set -euo pipefail
SESSION_NAME="my-project"
PROJECT_DIR="${HOME}/Projects/my-app"
# Human-facing 1-based windows/panes for this session
tmux_index_opts=( -w pane-base-index 1 )
if ! tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
echo "Building tmux workspace: $SESSION_NAME"
# -d: stay detached so this script continues
tmux new-session -d -s "$SESSION_NAME" -n editor -c "$PROJECT_DIR"
tmux set-option -t "$SESSION_NAME" base-index 1
tmux set-window-option -t "$SESSION_NAME:editor" pane-base-index 1
tmux send-keys -t "$SESSION_NAME:editor.1" "nvim ." C-m
tmux new-window -t "$SESSION_NAME" -n infra -c "$PROJECT_DIR"
tmux set-window-option -t "$SESSION_NAME:infra" pane-base-index 1
tmux send-keys -t "$SESSION_NAME:infra.1" "npm run dev" C-m
# -h: left | right
tmux split-window -h -t "$SESSION_NAME:infra.1" -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION_NAME:infra.2" "docker logs -f my-database" C-m
# -v: split the right pane into top / bottom
tmux split-window -v -t "$SESSION_NAME:infra.2" -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION_NAME:infra.3" "git status" C-m
tmux select-layout -t "$SESSION_NAME:infra" main-vertical
tmux select-window -t "$SESSION_NAME:editor"
fi
tmux attach-session -t "$SESSION_NAME"
How to run it
chmod +x setup_project.sh
./setup_project.sh
Second run: has-session succeeds, setup is skipped, you only attach. Your running editor is not rebuilt on top of itself.
Three facts the script depends on
new-session -d— build in the dark; attach last.-t session:window.pane— name the room, do not hope the “current” pane is correct.send-keys … C-m— inject keystrokes;C-mis Enter.
Replace nvim ., npm run dev, and docker logs with your commands. The shape is the lesson: detached construct, named targets, attach once.
A short lab
- On a disposable host or your laptop:
tmux new -s lab, runsleep 300, detach,exitthe SSH or close the window, attach again. The sleep remains. - Split twice. Name the feeling of getting lost. Create a second session (
tmux new -s other). Prefixs. That is segregation. - Run the script above against a throwaway directory. Kill the session (
tmux kill-session -t my-project) and run the script again. Rebuild should be boring. - Read
man tmuxsection on sessions and windows only. Stop before customizing the prefix. Mechanism before cosmetics.
Closing
We have treated tmux as it is used when the network is rude, the desk is small, two people must share a PTY, and the morning layout must not be folklore. The expert shift is not more key bindings. It is this sentence, used as a policy:
The session is the engine. The terminal is a viewfinder. If the layout matters, it is code.
Return to the terminal fathom when you need to say why detach survives hangup. Then come back here and script the next layout you have now built twice.