Skip to content

Stage 4 — Automation

Manual setup is fine when you have one project. With four or five, repeating the same tmux new, Ctrl+a c, rename, split, run-this-here dance every morning gets old fast. This stage automates it: one command per project, idempotent so it doesn’t matter if you’ve already started it, with a project picker menu on top.


After this stage you will be able to:

  • Write a startup script that brings up a complete tmux session for a project — windows, panes, running processes — in one command.
  • Make scripts idempotent: running them when the session is already up reattaches instead of creating a duplicate.
  • Use tmux’s session:window.pane targeting syntax to address any part of any session from a script.
  • Maintain a project picker menu that shows all projects with status, ready to attach.
  • Decide whether plugins like tmux-resurrect are worth adding (they usually aren’t, until they are).

  • Stages 1 through 3 complete — you have a working ~/.tmux.conf and you’re comfortable with windows, panes, and naming.
  • Basic shell scripting comfort (bash, if, variables).
  • A ~/bin directory on PATH for your scripts to live in.

The core idea is a script per project that builds out the session you’d otherwise build by hand. Save it once, run it forever.

Here’s the canonical version for the nafbi project:

~/bin/start-nafbi.sh
#!/bin/bash
# Bring up the tmux session for the NAFBI project.
SESSION="nafbi"
PROJECT_DIR="$HOME/all-business/nafbi"
# Idempotency: if the session already exists, just attach.
if tmux has-session -t "$SESSION" 2>/dev/null; then
tmux attach -t "$SESSION"
exit 0
fi
# Window 1: dev — split into editor + shell
tmux new-session -d -s "$SESSION" -n dev -c "$PROJECT_DIR"
tmux split-window -h -t "$SESSION:dev" -c "$PROJECT_DIR"
tmux select-pane -t "$SESSION:dev.1"
# Window 2: logs — auto-tail syslog
tmux new-window -t "$SESSION" -n logs -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:logs" "tail -f /var/log/syslog" C-m
# Window 3: claude — empty shell, ready for `claude`
tmux new-window -t "$SESSION" -n claude -c "$PROJECT_DIR"
# Window 4: git — auto-show status
tmux new-window -t "$SESSION" -n git -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:git" "git status" C-m
# Land on the dev window and attach
tmux select-window -t "$SESSION:dev"
tmux attach -t "$SESSION"

Run it:

Terminal window
chmod +x ~/bin/start-nafbi.sh
start-nafbi.sh

First run: builds the session, attaches you. Second run (after a detach): notices the session exists, just reattaches. Third run from a fresh terminal: same — reattaches.

That’s the pattern. Copy it for start-thrivesend.sh, start-parallelself.sh, start-ras-kruger.sh. Each one knows the project’s directory, its windows, and what to auto-run in each.


The argument that keeps appearing is -t "session:window.pane". Read it as:

session_name : window_name_or_index . pane_index

Examples:

  • nafbi — the session itself
  • nafbi:dev — window dev in session nafbi
  • nafbi:dev.1 — pane 1 of window dev in session nafbi
  • nafbi:2 — window 2 (by index) in session nafbi
  • nafbi:logs.0 — pane 0 of window logs (the first pane after a split is .0, the second is .1)

You’ll mostly use session:window (whole window) or session:window.pane (specific pane). The first pane in a window is .0 if pane-base-index is 0, or .1 if it’s 1 (the recommended config in Stage 3 sets it to 1).


The four building blocks you need for almost any startup script:

CommandWhat it does
tmux new-session -d -s NAME -n WIN -c DIRCreate a session detached, with a named first window in a directory
tmux new-window -t SESSION -n NAME -c DIRAdd a window to an existing session
tmux split-window -h -t TARGET -c DIRSplit a pane horizontally (-h = side-by-side) or vertically (-v = stacked)
tmux send-keys -t TARGET "command" C-mType a command into a target shell. C-m is Enter.

The send-keys ... C-m trick is how you get a shell to actually run a command — without C-m it just types the text and waits.


Convention: ~/bin/start-PROJECT.sh. Keep them all in one place so the project picker (next section) can discover them.

Make sure ~/bin is on PATH. In ~/.bashrc:

Terminal window
export PATH="$HOME/bin:$PATH"

Then source ~/.bashrc (or open a new shell). After that, start-nafbi.sh works from anywhere.


When you have five or six projects, even remembering which scripts exist gets tedious. A picker menu lists them all and shows running status.

~/bin/projects.sh
#!/bin/bash
# Interactive picker for project tmux sessions.
set -e
# Find all start-*.sh scripts in ~/bin
mapfile -t scripts < <(find "$HOME/bin" -maxdepth 1 -type f -name 'start-*.sh' | sort)
if [ ${#scripts[@]} -eq 0 ]; then
echo "No project scripts found in ~/bin/start-*.sh"
exit 1
fi
echo "Available projects:"
echo
for i in "${!scripts[@]}"; do
script="${scripts[$i]}"
# Extract the project name from start-PROJECT.sh
name="$(basename "$script" .sh)"
name="${name#start-}"
# Mark running vs stopped
if tmux has-session -t "$name" 2>/dev/null; then
status="[running]"
else
status="[stopped]"
fi
printf " %2d. %-20s %s\n" "$((i+1))" "$name" "$status"
done
echo
read -rp "Select project (number, or q to quit): " choice
if [[ "$choice" == "q" || -z "$choice" ]]; then
exit 0
fi
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#scripts[@]} )); then
echo "Invalid selection."
exit 1
fi
script="${scripts[$((choice-1))]}"
exec "$script"

Make it executable:

Terminal window
chmod +x ~/bin/projects.sh

Then either run projects.sh directly, or alias it to something short — alias p=projects.sh in ~/.bashrc.

The output looks like:

Available projects:
1. nafbi [running]
2. parallelself [stopped]
3. ras-kruger [stopped]
4. thrivesend [running]
Select project (number, or q to quit):

Pick a number, the matching start-*.sh runs, and you’re in.


Auto-run dev servers cautiously. If a window has tmux send-keys ... "npm run dev" C-m, the dev server starts every time you bring the session up. That’s usually what you want — but if a session has been running for hours, restarting the dev server might lose state. For long-lived processes, prefer to start them manually the first time.

Use if to guard expensive setup. Inside the script, after tmux has-session succeeds (session exists), you can also re-run cheap idempotent commands (e.g. git status in a new window) without recreating the session. The simple attach && exit 0 pattern in the example above is the cleanest default.

Watch out for paths that don’t exist. If PROJECT_DIR doesn’t exist on the machine, tmux new-session -c "$PROJECT_DIR" will silently fall back to the home directory. Add a guard:

Terminal window
if [ ! -d "$PROJECT_DIR" ]; then
echo "Project directory $PROJECT_DIR not found."
exit 1
fi

Different hosts, same script. If a script needs to work on both Mac and the server, use $HOME and conditional paths rather than hardcoded /Users/... or /home/....


tmux has a plugin manager — TPM, the Tmux Plugin Manager — that lets you install community plugins. Don’t reach for this until the foundation is solid, but two plugins are genuinely useful for the right person.

Saves all your sessions, windows, panes, and (optionally) running processes to disk. Lets you restore them across reboots.

KeystrokeAction
Ctrl+a Ctrl+sSave all sessions
Ctrl+a Ctrl+rRestore from save

Useful if you reboot regularly and don’t want to re-run startup scripts. Less useful if you have good startup scripts and rarely reboot.

Pairs with tmux-resurrect. Auto-saves every 15 minutes and can auto-restore on tmux start. Set-and-forget version of resurrect.

Terminal window
# Install TPM
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

Add to the bottom of ~/.tmux.conf:

# ── Plugins ──────────────────────────────────────────────
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
# Continuum settings
set -g @continuum-restore 'on'
set -g @continuum-save-interval '15'
# Initialize TPM (KEEP THIS LAST IN THE FILE)
run '~/.tmux/plugins/tpm/tpm'

Reload config (Ctrl+a r), then install plugins:

Ctrl+a I

(That’s a capital I, for Install.) TPM downloads the listed plugins and reloads.


If you’ve worked through all four stages, you can:

  • Survive on a remote server with no fear of losing work.
  • Live in tmux daily for editing, monitoring, and version control.
  • Make tmux match your taste with config you understand.
  • Spin up an entire project workspace in one command.

That’s everything most people need. Use the Cheat Sheet as your daily reference and the Troubleshooting page when something feels wrong.