C.W.K.
Stream
Lesson 03 of 03 · published

Session Management and Scripted Layouts

~12 min · tmux, scripts, tmuxinator, automation

Level 0Trapped
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The 5-line shell script that becomes your dev environment

Once you've used tmux for a week you'll notice a pattern: every morning you open the same project, create the same windows, run the same commands. tmux's command-line interface is rich enough to automate the whole sequence in a shell script. Run the script; the workspace is up.

The basic recipe

tmux's command-line accepts subcommands like new-session, send-keys, split-window. -d creates the session detached so you can keep configuring it; -t targets a session/window/pane; send-keys "cmd" C-m sends a string and a Carriage-Return (Enter).

tmuxinator — when shell scripts get gnarly

For complex layouts, tmuxinator is the YAML-based alternative. You describe the workspace declaratively; tmuxinator start project brings it up. It's a Ruby gem, and there are similar tools in other languages (tmuxp in Python). For most personal projects, a 10-line shell script is plenty.

Nested tmux over SSH

Common scenario: tmux running locally, you SSH into a server running its own tmux. Now Ctrl-a is ambiguous — does it go to your local prefix or the remote one? Two clean solutions:

  • Different prefixes per machine. Local tmux uses Ctrl-a, remote tmux uses Ctrl-b (the default). No conflict.
  • Send-prefix binding. If you must use the same prefix on both, configure each so Ctrl-a a sends a literal Ctrl-a to whatever's nested below. Hit it twice to talk to the inner tmux.
Boilerplate is a smell. If you find yourself doing the same series of prefix + commands every morning, that's a script waiting to be written. Scripts cost minutes to write and save those minutes every day for the rest of the project.

The discoverable detach

tmux detach-client, tmux switch-client -t name, tmux send-keys -t name 'cmd' Enter — every interactive key has a non-interactive command-line counterpart. That's how you automate.

Code

dev-setup.sh — a real workspace script·bash
#!/usr/bin/env bash
# dev-setup.sh — open a 3-window dev workspace
set -euo pipefail

SESSION="myproject"
PROJECT_DIR="${HOME}/projects/myproject"

# Don't recreate if it already exists
if tmux has-session -t "$SESSION" 2>/dev/null; then
  exec tmux attach -t "$SESSION"
fi

# Window 1: editor
tmux new-session -d -s "$SESSION" -c "$PROJECT_DIR" -n editor
tmux send-keys -t "$SESSION:editor" "nvim ." C-m

# Window 2: dev server
tmux new-window -t "$SESSION" -n server -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:server" "npm run dev" C-m

# Window 3: shell with two panes
tmux new-window -t "$SESSION" -n shell -c "$PROJECT_DIR"
tmux split-window -h -t "$SESSION:shell" -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION:shell.0" "git status" C-m

# Land on the editor window
tmux select-window -t "$SESSION:editor"
tmux attach -t "$SESSION"
tmuxinator — the YAML version of the same layout·yaml
# ~/.tmuxinator/myproject.yml
name: myproject
root: ~/projects/myproject

windows:
  - editor:
      panes:
        - nvim .
  - server:
      panes:
        - npm run dev
  - shell:
      layout: even-horizontal
      panes:
        - git status
        - 
Nested-tmux SSH config·tmux
# Local tmux uses Ctrl-a; press it twice to send a literal
# Ctrl-a to whatever's running below (which can be a remote tmux
# still using Ctrl-b, or another instance using Ctrl-a).
bind -n C-a send-prefix

# Or: clean separation. Local: Ctrl-a. Remote: stay on default Ctrl-b.
# Then no conflict — each tmux receives its own prefix.
Useful one-shot tmux commands from scripts·bash
# Send a command into a running session/window/pane
tmux send-keys -t myproject:editor.0 'echo hi' C-m

# Switch the attached client to a window
tmux select-window -t myproject:server

# Save current pane contents to a file
tmux capture-pane -p -t myproject:editor > /tmp/editor.log

# List sessions in a script-friendly format
tmux list-sessions -F '#{session_name}'

External links

Exercise

Pick a project you work on regularly. Write a dev-setup.sh for it modeled on the script above: session name = project name, two or three windows for the tasks you actually run, the editor opens to the project root. Make it idempotent. Run it; detach; run it again — it should reattach instead of duplicating. Commit the script with the project. Future you (and any teammate) wins.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.