Short hand / inline style¶
tmuxp has a short-hand syntax for those who wish to keep their workspace punctual.
session_name: shorthands windows: - window_name: long form panes: - shell_command: - echo 'did you know' - echo 'you can inline' - shell_command: echo 'single commands' - echo 'for panes'
{ "windows": [ { "panes": [ { "shell_command": [ "echo 'did you know'", "echo 'you can inline'" ] }, { "shell_command": "echo 'single commands'" }, "echo 'for panes'" ], "window_name": "long form" } ], "session_name": "shorthands" }
Blank panes¶
No need to repeat pwd or a dummy command. A null, 'blank',
'pane' are valid.
Note '' counts as an empty carriage return.
session_name: Blank pane test windows: # Emptiness will simply open a blank pane, if no shell_command_before. # All these are equivalent - window_name: Blank pane test panes: - - pane - blank - window_name: More blank panes panes: - null - shell_command: - shell_command: - # an empty string will be treated as a carriage return - window_name: Empty string (return) panes: - "" - shell_command: "" - shell_command: - "" # a pane can have other options but still be blank - window_name: Blank with options panes: - focus: true - start_directory: /tmp
{ "windows": [ { "panes": [ null, "pane", "blank" ], "window_name": "Blank pane test" }, { "panes": [ null, { "shell_command": null }, { "shell_command": [ null ] } ], "window_name": "More blank panes" }, { "panes": [ "", { "shell_command": "" }, { "shell_command": [ "" ] } ], "window_name": "Empty string (return)" }, { "panes": [ { "focus": true }, { "start_directory": "/tmp" } ], "window_name": "Blank with options" } ], "session_name": "Blank pane test" }
2 panes¶
session_name: 2-pane-vertical windows: - window_name: my test window panes: - echo hello - echo hello
{ "windows": [ { "panes": [ "echo hello", "echo hello" ], "window_name": "my test window" } ], "session_name": "2-pane-vertical" }
3 panes¶
session_name: 3-panes windows: - window_name: dev window layout: main-vertical shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep \.log - echo hello - echo hello
{ "windows": [ { "panes": [ { "shell_command": [ "cd /var/log", "ls -al | grep \\.log" ] }, "echo hello", "echo hello" ], "shell_command_before": [ "cd ~/" ], "layout": "main-vertical", "window_name": "dev window" } ], "session_name": "3-panes" }
4 panes¶
session_name: 4-pane-split windows: - window_name: dev window layout: tiled shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep \.log - echo hello - echo hello - echo hello
{ "windows": [ { "panes": [ { "shell_command": [ "cd /var/log", "ls -al | grep \\.log" ] }, "echo hello", "echo hello", "echo hello" ], "shell_command_before": [ "cd ~/" ], "layout": "tiled", "window_name": "dev window" } ], "session_name": "4-pane-split" }
Start Directory¶
Equivalent to tmux new-window -c <start-directory>.
session_name: start directory start_directory: /var/ windows: - window_name: should be /var/ panes: - shell_command: - echo "\033c - it trickles down from session-level" - echo hello - window_name: should be /var/log start_directory: log panes: - shell_command: - echo '\033c - window start_directory concatenates to session start_directory - if it is not absolute' - echo hello - window_name: should be ~ start_directory: "~" panes: - shell_command: - 'echo \\033c ~ has precedence. note: remember to quote ~ in YAML' - echo hello - window_name: should be /bin start_directory: /bin panes: - echo '\033c absolute paths also have precedence.' - echo hello - window_name: should be workspace file's dir start_directory: ./ panes: - shell_command: - echo '\033c - ./ is relative to workspace file location - ../ will be parent of workspace file - ./test will be \"test\" dir inside dir of workspace file' - shell_command: - echo '\033c - This way you can load up workspaces from projects and maintain - relative paths.'
{ "windows": [ { "panes": [ { "shell_command": [ "echo \"\\033c", "it trickles down from session-level\"" ] }, "echo hello" ], "window_name": "should be /var/" }, { "panes": [ { "shell_command": [ "echo '\\033c", "window start_directory concatenates to session start_directory", "if it is not absolute'" ] }, "echo hello" ], "start_directory": "log", "window_name": "should be /var/log" }, { "panes": [ { "shell_command": [ "echo \\\\033c ~ has precedence. note: remember to quote ~ in YAML" ] }, "echo hello" ], "start_directory": "~", "window_name": "should be ~" }, { "panes": [ "echo '\\033c absolute paths also have precedence.'", "echo hello" ], "start_directory": "/bin", "window_name": "should be /bin" }, { "panes": [ { "shell_command": [ "echo '\\033c", "./ is relative to workspace file location", "../ will be parent of workspace file", "./test will be \\\"test\\\" dir inside dir of workspace file'" ] }, { "shell_command": [ "echo '\\033c", "This way you can load up workspaces from projects and maintain", "relative paths.'" ] } ], "start_directory": "./", "window_name": "should be config's dir" } ], "session_name": "start directory", "start_directory": "/var/" }
Environment variable replacing¶
tmuxp will replace environment variables wrapped in curly brackets for values of these settings:
start_directorybefore_scriptsession_namewindow_nameshell_command_beforeglobal_optionsoptionsin session scope and window scope
tmuxp replaces these variables before-hand with variables in the
terminal tmuxp invokes in.
In this case of this example, assuming the username “user”:
$ MY_ENV_VAR=foo tmuxp load examples/env-variables.yaml
and your session name will be session - user (foo).
Shell variables in shell_command do not support this type of
concatenation. shell_command and shell_command_before both
support normal shell variables, since they are sent into panes
automatically via send-key in tmux(1). See ls $PWD in
example.
If you have a special case and would like to see behavior changed, please make a ticket on the issue tracker.
start_directory: "${PWD}/test" shell_command_before: "echo ${PWD}" before_script: "${MY_ENV_VAR}/test3.sh" session_name: session - ${USER} (${MY_ENV_VAR}) windows: - window_name: editor panes: - shell_command: - tail -F /var/log/syslog start_directory: /var/log - window_name: logging for ${USER} options: automatic-rename: true panes: - shell_command: - htop - ls $PWD
{ "before_script": "${MY_ENV_VAR}/test3.sh", "windows": [ { "panes": [ { "shell_command": [ "tail -F /var/log/syslog" ] } ], "start_directory": "/var/log", "window_name": "editor" }, { "panes": [ { "shell_command": [ "htop", "ls $PWD" ] } ], "window_name": "logging for ${USER}", "options": { "automatic-rename": true } } ], "shell_command_before": "echo ${PWD}", "start_directory": "${PWD}/test", "session_name": "session - ${USER} (${MY_ENV_VAR})" }
Environment variables¶
tmuxp will set session, window and pane environment variables.
Note
Setting environment variables for windows and panes requires tmuxp 1.19 or newer.
session_name: Environment variables test environment: EDITOR: /usr/bin/vim DJANGO_SETTINGS_MODULE: my_app.settings.local SERVER_PORT: "8009" windows: - window_name: Django project panes: - ./manage.py runserver 0.0.0.0:${SERVER_PORT} - window_name: Another Django project environment: DJANGO_SETTINGS_MODULE: my_app.settings.local SERVER_PORT: "8010" panes: - ./manage.py runserver 0.0.0.0:${SERVER_PORT} - environment: DJANGO_SETTINGS_MODULE: my_app.settings.local-testing SERVER_PORT: "8011" shell_command: ./manage.py runserver 0.0.0.0:${SERVER_PORT}
{ "environment": { "EDITOR": "/usr/bin/vim", "DJANGO_SETTINGS_MODULE": "my_app.settings.local", "SERVER_PORT": "8009" }, "windows": [ { "panes": [ "./manage.py runserver 0.0.0.0:${SERVER_PORT}" ], "window_name": "Django project" }, { "environment": { "DJANGO_SETTINGS_MODULE": "my_app.settings.local", "SERVER_PORT": "8010" }, "panes": [ "./manage.py runserver 0.0.0.0:${SERVER_PORT}", { "environment": { "DJANGO_SETTINGS_MODULE": "my_app.settings.local-testing", "SERVER_PORT": "8011" }, "shell_command": "./manage.py runserver 0.0.0.0:${SERVER_PORT}" } ], "window_name": "Another Django project" } ], "session_name": "Environment variables test" }
Focusing¶
tmuxp allows focus: true for assuring windows and panes are attached /
selected upon loading.
session_name: focus windows: - window_name: attached window focus: true panes: - shell_command: - echo hello - echo 'this pane should be selected on load' focus: true - shell_command: - cd /var/log - echo hello - window_name: second window shell_command_before: cd /var/log panes: - pane - shell_command: - echo 'this pane should be focused, when window switched to first time' focus: true - pane
{ "windows": [ { "panes": [ { "shell_command": [ "echo hello", "echo 'this pane should be selected on load'" ], "focus": true }, { "shell_command": [ "cd /var/log", "echo hello" ] } ], "window_name": "attached window on load", "focus": true }, { "panes": [ "pane", { "shell_command": [ "echo 'this pane should be focused, when window switched to first time'" ], "focus": true }, "pane" ], "shell_command_before": "cd /var/www", "window_name": "second window" } ], "session_name": "focus window and pane when loading sessions" }
Terminal History¶
tmuxp allows suppress_history: false to override the default command /
suppression when building the workspace.
This will add the shell_command to the shell history in the pane.
The suppression of the shell_command commands from the shell’s history
occurs by prefixing the commands with a space when suppress_history: true.
Accordingly, this functionality depends on the shell being appropriately
configured: bash requires the shell variable HISTCONTROL to be set and
include either of the values ignorespace or ignoreboth (to also ignore
sequential duplicate commands), and zsh requires setopt HIST_IGNORE_SPACE.
session_name: suppress suppress_history: false windows: - window_name: appended focus: true suppress_history: false panes: - echo "window in the history!" - window_name: suppressed suppress_history: true panes: - echo "window not in the history!" - window_name: default panes: - echo "session in the history!" - window_name: mixed suppress_history: false panes: - shell_command: - echo "command in the history!" suppress_history: false - shell_command: - echo "command not in the history!" suppress_history: true - shell_command: - echo "window in the history!"
{ "windows": [ { "panes": [ "echo 'window in the history!'" ], "focus": true, "suppress_history": false, "window_name": "appended" }, { "panes": [ "echo 'window not in the history!'" ], "suppress_history": true, "window_name": "suppressed" }, { "panes": [ "echo 'session in the history!'" ], "window_name": "default" }, { "panes": [ { "shell_command": "echo 'command in the history!'", "suppress_history": false }, { "shell_command": "echo 'command not in the history!'", "suppress_history": true }, { "shell_command": "echo 'window not in the history!'" } ], "suppress_history": true, "window_name": "mixed" } ], "suppress_history": false, "session_name": "suppress" }
Skip command execution¶
See more at Skip command execution.
Note
Experimental setting: behavior and api is subject to change until stable.
Added in version 1.10.0: enter: false option. Pane-level support.
Omit sending enter to key commands. Equivalent to
send_keys(enter=False).
session_name: Skip command execution (command-level) windows: - panes: - shell_command: # You can see this - echo "___$((11 + 1))___" # This is skipped - cmd: echo "___$((1 + 3))___" enter: false
{ "session_name": "Skip command execution (command-level)", "windows": [ { "panes": [ { "shell_command": [ "echo \"___$((11 + 1))___\"", { "cmd": "echo \"___$((1 + 3))___\"", "enter": false } ] } ] } ] }
session_name: Skip command execution (pane-level) windows: - panes: - shell_command: echo "___$((1 + 3))___" enter: false - shell_command: - echo "___$((1 + 3))___"\; - echo "___$((1 + 3))___" enter: false
{ "session_name": "Skip command execution (pane-level)", "windows": [ { "panes": [ { "shell_command": "echo \"___$((1 + 3))___\"", "enter": false }, { "shell_command": [ "echo \"___$((1 + 3))___\"\\;", "echo \"___$((1 + 3))___\"" ], "enter": false } ] } ] }
Pausing commands¶
Note
Experimental setting: behavior and api is subject to change until stable.
Added in version 1.10.0: sleep_before and sleep_after options added. Pane and command-level support.
Warning
Blocking. This will delay loading as it runs synchronously for each pane as asyncio) is not implemented yet.
Omit sending enter to key commands. Equivalent to having
a time.sleep before and after send_keys.
This is especially useful for expensive commands where the terminal needs some breathing room (virtualenv, poetry, pipenv, uv, sourcing a configuration, launching a tui app, etc).
session_name: virtualenv shell_command_before: # - cmd: source $(poetry env info --path)/bin/activate # - cmd: source `pipenv --venv`/bin/activate - cmd: source .venv/bin/activate sleep_before: 1 sleep_after: 1 windows: - panes: - shell_command: - ./manage.py runserver
session_name: Pause / skip command execution (command-level) windows: - panes: - shell_command: # Executes immediately - echo "___$((11 + 1))___" # Delays before sending 2 seconds - cmd: echo "___$((1 + 3))___" sleep_before: 2 # Executes immediately - cmd: echo "___$((1 + 3))___" # Pauses 2 seconds after - cmd: echo "Stuff rendering here!" sleep_after: 2 # Executes after earlier commands (after 2 sec) - cmd: echo "2 seconds later"
{ "session_name": "Pause / skip command execution (command-level)", "windows": [ { "panes": [ { "shell_command": [ "echo \"___$((11 + 1))___\"", { "cmd": "echo \"___$((1 + 3))___\"", "sleep_before": 2 }, { "cmd": "echo \"___$((1 + 3))___\"" }, { "cmd": "echo \"Stuff rendering here!\"", "sleep_after": 2 }, { "cmd": "echo \"2 seconds later\"" } ] } ] } ] }
session_name: Pause / skip command execution (pane-level) windows: - panes: - # Wait 2 seconds before sending all commands in this pane sleep_before: 2 shell_command: - echo "___$((11 + 1))___" - cmd: echo "___$((1 + 3))___" - cmd: echo "___$((1 + 3))___" - cmd: echo "Stuff rendering here!" - cmd: echo "2 seconds later"
{ "session_name": "Pause / skip command execution (pane-level)", "windows": [ { "panes": [ { "sleep_before": 2, "shell_command": [ "echo \"___$((11 + 1))___\"", { "cmd": "echo \"___$((1 + 3))___\"" }, { "cmd": "echo \"___$((1 + 3))___\"" }, { "cmd": "echo \"Stuff rendering here!\"" }, { "cmd": "echo \"2 seconds later\"" } ] } ] } ] }
Window Index¶
You can specify a window’s index using the window_index property. Windows
without window_index will use the lowest available window index.
session_name: Window index example windows: - window_name: zero panes: - echo "this window's index will be zero" - window_name: five panes: - echo "this window's index will be five" window_index: 5 - window_name: one panes: - echo "this window's index will be one"
{ "windows": [ { "panes": [ "echo \"this window's index will be zero\"" ], "window_name": "zero" }, { "panes": [ "echo \"this window's index will be five\"" ], "window_index": 5, "window_name": "five" }, { "panes": [ "echo \"this window's index will be one\"" ], "window_name": "one" } ], "session_name": "Window index example" }
Shell per pane¶
Every pane can have its own shell or application started. This allows for usage
of the remain-on-exit setting to be used properly, but also to have
different shells on different panes.
session_name: Pane shell example windows: - window_name: first window_shell: /usr/bin/python2 layout: even-vertical suppress_history: false options: remain-on-exit: true panes: - shell: /usr/bin/python3 shell_command: - print('This is python 3') - shell: /usr/bin/vim -u none shell_command: - iAll panes have the `remain-on-exit` setting on. - When you exit out of the shell or application, the panes will remain. - Use tmux command `:kill-pane` to remove the pane. - Use tmux command `:respawn-pane` to restart the shell in the pane. - Use <Escape> and then `:q!` to get out of this vim window. :-) - shell_command: - print('Hello World 2') - shell: /usr/bin/top
{ "session_name": "Pane shell example", "windows": [ { "window_name": "first", "window_shell": "/usr/bin/python2", "layout": "even-vertical", "suppress_history": false, "options": { "remain-on-exit": true }, "panes": [ { "shell": "/usr/bin/python3", "shell_command": [ "print('This is python 3')" ] }, { "shell": "/usr/bin/vim -u none", "shell_command": [ "iAll panes have the `remain-on-exit` setting on.", "When you exit out of the shell or application, the panes will remain.", "Use tmux command `:kill-pane` to remove the pane.", "Use tmux command `:respawn-pane` to restart the shell in the pane.", "Use <Escape> and then `:q!` to get out of this vim window. :-)" ] }, { "shell_command": [ "print('Hello World 2')" ] }, { "shell": "/usr/bin/top" } ] } ] }
Set tmux options¶
Works with global (server-wide) options, session options and window options.
Including automatic-rename, default-shell,
default-command, etc.
session_name: test window options start_directory: "~" global_options: default-shell: /bin/sh default-command: /bin/sh options: main-pane-height: ${MAIN_PANE_HEIGHT} # works with env variables windows: - layout: main-horizontal options: automatic-rename: on panes: - shell_command: - man echo start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo"
{ "windows": [ { "panes": [ { "shell_command": [ "man echo" ], "start_directory": "~" }, { "shell_command": [ "echo \"hey\"" ] }, { "shell_command": [ "echo \"moo\"" ] } ], "layout": "main-horizontal", "options": { "automatic-rename": true } } ], "session_name": "test window options", "start_directory": "~", "global_options": { "default-shell": "/bin/sh", "default-command": "/bin/sh" }, "options": { "main-pane-height": "${MAIN_PANE_HEIGHT}" } }
Set window options after pane creation¶
Apply window options after panes have been created. Useful for
synchronize-panes option after executing individual commands in each
pane during creation.
session_name: 2-pane-synchronized windows: - window_name: Two synchronized panes panes: - ssh server1 - ssh server2 options_after: synchronize-panes: on
{ "session_name": "2-pane-synchronized", "windows": [ { "window_name": "Two synchronized panes", "panes": [ "ssh server1", "ssh server2" ], "options_after": { "synchronize-panes": true } } ] }
Main pane height¶
Percentage¶
Added in version 1.46.0: Before this, tmuxp layouts would not detect the terminal’s size.
session_name: main-pane-height start_directory: "~" windows: - layout: main-horizontal options: main-pane-height: 67% panes: - shell_command: - top start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" window_name: my window name
{ "windows": [ { "panes": [ { "shell_command": [ "top" ], "start_directory": "~" }, { "shell_command": [ "echo \"hey\"" ] }, { "shell_command": [ "echo \"moo\"" ] } ], "layout": "main-horizontal", "options": { "main-pane-height": "67%" }, "window_name": "editor" } ], "session_name": "main pane height", "start_directory": "~" }
Rows¶
session_name: main-pane-height start_directory: "~" windows: - layout: main-horizontal options: main-pane-height: 30 panes: - shell_command: - top start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" window_name: my window name
{ "windows": [ { "panes": [ { "shell_command": [ "top" ], "start_directory": "~" }, { "shell_command": [ "echo \"hey\"" ] }, { "shell_command": [ "echo \"moo\"" ] } ], "layout": "main-horizontal", "options": { "main-pane-height": 30 }, "window_name": "editor" } ], "session_name": "main pane height", "start_directory": "~" }
Super-advanced dev environment¶
session_name: tmuxp start_directory: ./ # load session relative to config location (project root). shell_command_before: - uv virtualenv --quiet > /dev/null 2>&1 && clear windows: - window_name: tmuxp focus: True layout: main-horizontal options: main-pane-height: 67% panes: - focus: true - pane - just watch-mypy - just watch-test - window_name: docs layout: main-horizontal options: main-pane-height: 67% start_directory: docs/ panes: - focus: true - pane - pane - just start
{ "session_name": "tmuxp", "start_directory": "./", "shell_command_before": [ "uv virtualenv --quiet > /dev/null 2>&1 && clear" ], "windows": [ { "window_name": "tmuxp", "focus": true, "layout": "main-horizontal", "options": { "main-pane-height": "67%" }, "panes": [ { "focus": true }, "pane", "just watch-mypy", "just watch-test" ] }, { "window_name": "docs", "layout": "main-horizontal", "options": { "main-pane-height": "67%" }, "start_directory": "docs/", "panes": [ { "focus": true }, "pane", "pane", "just start" ] } ] }
Multi-line commands¶
You can use YAML’s multiline syntax to easily split multiple commands into the same shell command: https://stackoverflow.com/a/21699210
session_name: my project shell_command_before: - > [ -d `.venv/bin/activate` ] && source .venv/bin/activate && reset - sleep 1 windows: - window_name: first window layout: main-horizontal focus: true panes: - focus: True - blank - > uv run ./manage.py migrate && npm -C js run start - uv run ./manage.py runserver options: main-pane-height: 35
Bootstrap project before launch¶
You can use before_script to run a script before the tmux session
starts building. This can be used to start a script to create a virtualenv
or download a virtualenv/rbenv/package.json’s dependency files before
tmuxp even begins building the session.
It works by using the Exit Status code returned by a script. Your script can be any type, including bash, python, ruby, etc.
A successful script will exit with a status of 0.
Important: the script file must be chmod executable +x or 755.
Run a python script (and check for it’s return code), the script is
relative to the .tmuxp.yaml’s root (Windows and panes omitted in
this example):
session_name: my session before_script: ./bootstrap.py # ... the rest of your workspace
{ "session_name": "my session", "before_script": "./bootstrap.py" }
Run a shell script + check for return code on an absolute path. (Windows and panes omitted in this example)
session_name: another example before_script: /absolute/path/this.sh # abs path to shell script # ... the rest of your workspace
{ "session_name": "my session", "before_script": "/absolute/path/this.sh" }
Per-project tmuxp workspaces¶
You can load your software project in tmux by placing a .tmuxp.yaml or
.tmuxp.json in the project’s workspace and loading it.
tmuxp supports loading workspace via absolute filename with tmuxp load
and via $ tmuxp load . if workspace is in the directory.
$ tmuxp load ~/workspaces/myproject.yaml
See examples of tmuxp in the wild. Have a project workspace to show off?
Edit this page.
https://github.com/vcs-python/vcspull/blob/master/.tmuxp.yaml
https://github.com/tony/sphinxcontrib-github/blob/master/.tmuxp.yaml
You can use start_directory: ./ to make the directories relative to
the workspace file / project root.
Bonus: pipenv auto-bootstrapping¶
Added in version 1.3.4: before_script CWD’s into the root (session)-level
start_directory.
If you use pipenv / poetry / uv, you can use a script like this to ensure your packages are installed:
# assuming your .tmuxp.yaml is in your project root directory session_name: my pipenv project start_directory: ./ before_script: pipenv install --dev --skip-lock # ensure dev deps install windows: - window_name: django project focus: true panes: - blank - pipenv run ./manage.py runserver
You can also source yourself into the virtual environment using
shell_command_before:
# assuming your .tmuxp.yaml is in your project root directory session_name: my pipenv project start_directory: ./ before_script: pipenv install --dev --skip-lock # ensure dev deps install shell_command_before: - '[ -d `pipenv --venv` ] && source `pipenv --venv`/bin/activate && reset' windows: - window_name: django project focus: true panes: - blank - ./manage.py runserver
Kung fu¶
Note
tmuxp sessions can be scripted in python. The first way is to use the
ORM in the API Reference. The second is to pass a dict into
WorkspaceBuilder with a correct schema.
See: tmuxp.validation.validate_schema().
Add yours? Submit a pull request to the github site!