tmux & tmuxp Skill
Create, edit, debug, and optimize tmux sessions via tmuxp YAML configurations.
Quick Decisions
| Task | Approach |
|---|---|
| New project workspace | Create tmuxp YAML from template |
| Fix session load error | Check session_name, YAML syntax, tool availability |
| Multi-environment K8s | Use environment vars + per-env windows with safety guards |
| Simple dev setup | 2-3 windows: editor, server, terminal |
| Complex infra | before_script validation + helper scripts + monitoring windows |
| Capture existing layout | tmuxp freeze then clean up the output |
Session Name Rules
tmux session names cannot contain periods (.) or colons (:).
Common pitfall: using ${USER} in session_name when the username contains periods (e.g., first.last). Always use a static name or sanitize:
# BAD - breaks if USER contains periods
session_name: ${USER}-project
# GOOD - static name
session_name: project-dev
# GOOD - sanitized
session_name: project-${USER//\./-}
Configuration Structure
session_name: project-name # Required. No periods or colons.
start_directory: ~/Projects/foo # Default working dir for all windows
environment: # Session-wide env vars
PROJECT_ROOT: ~/Projects/foo
suppress_history: false # Whether to hide commands from shell history
before_script: | # Runs before session creation. Exit 1 = abort.
echo "Validating..."
after_script: | # Runs after session is destroyed
echo "Cleaning up..."
windows:
- window_name: editor # Window identifier
focus: true # Make this the active window on load
layout: main-vertical # Pane layout
start_directory: ~/Projects/foo/src
options:
main-pane-width: 70% # Layout-specific options
shell_command_before: # Runs in ALL panes before pane commands
- source ~/.zshrc
panes:
- focus: true # Active pane within window
shell_command:
- vim .
- shell_command:
- npm test -- --watch
Layouts
| Layout | Use For | Pane Arrangement |
|---|---|---|
main-vertical |
Editor + sidebars | Large left, stacked right |
main-horizontal |
Logs + status | Large top, split bottom |
even-horizontal |
Equal side-by-side | Equal horizontal splits |
even-vertical |
Equal stacked | Equal vertical splits |
tiled |
Monitoring dashboards | Grid of equal panes |
Control main pane size via options:
options:
main-pane-width: 70% # For main-vertical
main-pane-height: 65% # For main-horizontal
Capture a custom layout from a running session:
tmux display-message -p '#{window_layout}'
# Returns: "bb62,159x48,0,0{79x48,0,0,79x48,80,0}"
Pane Definitions
panes:
# Simple command
- vim README.md
# Multiple commands
- shell_command:
- cd ~/project
- source .venv/bin/activate
- python app.py
# Empty pane
- null # or: blank, pane
# With focus
- focus: true
shell_command:
- k9s
Environment Variables
environment:
# Static values
PROJECT_NAME: my-app
# Reference existing vars (expanded at load time)
HOME_DIR: ${HOME}
# Multi-environment pattern
K8S_CTX_DEV: aks-myapp-dev
K8S_CTX_STG: aks-myapp-stg
K8S_CTX_PRD: aks-myapp-prd
# Defaults
EDITOR: ${EDITOR:-vim}
Never hardcode secrets. Reference env vars from the shell: ${AZURE_SUBSCRIPTION_ID}.
before_script Validation
Use before_script to validate prerequisites. Exit 1 aborts session creation:
before_script: |
# Check project exists
[ -d "$PROJECT_ROOT" ] || { echo "Project not found"; exit 1; }
# Check required tools
for tool in kubectl terraform docker; do
command -v $tool >/dev/null || echo "Warning: $tool not found"
done
# Check connectivity
kubectl cluster-info >/dev/null 2>&1 || echo "Warning: Cannot reach cluster"
Production Safety Patterns
Protect production environments with read-only access and warnings:
- window_name: k8s-prod
panes:
- shell_command:
- echo "PRODUCTION - READ-ONLY ACCESS"
- echo "DO NOT use: apply, delete, edit, patch"
- kubectl config use-context $K8S_CTX_PRD
- k9s --readonly
CLI Commands
tmuxp load config-name # Load from ~/.tmuxp/
tmuxp load ./path/to/file.yaml # Load from path
tmuxp load -y config-name # Skip confirmation prompt
tmuxp load -d config-name # Load detached (background)
tmuxp ls # List available configs
tmuxp freeze session-name # Capture running session to YAML
tmuxp convert file.json # Convert JSON config to YAML
tmuxp edit config-name # Edit config in $EDITOR
tmuxp debug-info # Show environment info
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
BadSessionName: contains periods |
session_name has . (often from ${USER}) |
Remove ${USER} prefix or sanitize |
BadSessionName: contains colons |
session_name has : |
Remove colons from name |
| Session already exists | Duplicate session_name | Kill old: tmux kill-session -t name |
| Commands not executing | Shell compatibility | Test commands manually first |
| Layout broken | Terminal too small for layout | Use predefined layouts or test with tmuxp load -d |
| Env vars not expanding | Wrong syntax | Use ${VAR} not $VAR in YAML values |
Debug: tmuxp -v load config.yaml for verbose output.
References
- WORKFLOWS.md - Common workflow patterns (dev, infra, monitoring)
- BEST-PRACTICES.md - Production patterns, safety, organization
- templates/ - Ready-to-use config templates
Workflow: Create New Config
- Identify the project type (dev, infra, monitoring, mixed)
- Choose a template from
templates/ - Set session_name (no periods/colons), start_directory, environment vars
- Design windows by function (editor, server, logs, k8s, etc.)
- Pick layouts matching each window's purpose
- Add before_script validation if the project has external dependencies
- Add production safety guards for any prod-access windows
- Test:
tmuxp load -d config.yamlthentmux attach -t session-name
Gotchas
- Session names with periods break the unix-socket path:
${USER}containing.(e.g.first.last) producesBadSessionNamebecause tmux uses the name in/tmp/tmux-UID/socket path. Use a static name or${USER//\./-}sanitization. before_scriptruns in a fresh shell, not your interactive zsh: Aliases, functions, and.zshrc-sourced env vars are absent.command -vworks butmyaliasdoes not. Source~/.zshrcexplicitly if you depend on it.shell_command_beforeruns in EVERY pane of the window: Heavy commands (sourcing 500ms+ of zsh config, activating venvs) multiply latency — a 4-pane window adds ~2s to session load. Use per-paneshell_commandinstead when only one pane needs it.tmuxp freezecaptures live state, not intent: Output includes the random working directories, history-expanded commands, and the literal pane sizes — review and clean before committing. Frozen YAML is a starting point, not a finished config.- Env var expansion happens at YAML load, not pane start:
environment: FOO: ${BAR}resolves$BARfrom the shell that invokedtmuxp load. If$BARis unset there, it stays empty even if a later pane defines it. focus: trueon multiple panes silently picks the last one: No error, no warning — the file just looks misconfigured at runtime. Validate withgrep -c "focus: true"per window before debugging.