A tmux plugin for saving, restoring, and templating your tmux workspace. Survive crashes, reboots, and context switches — your sessions come back exactly as you left them.
- Save & Restore — snapshots of all sessions, windows, panes, names, layout, and cwd
- Templates — YAML-defined workspaces with trusted commands, variables, per-host overrides
- tmux-revive picker — fzf-based interactive picker for managing sessions, snapshots, and templates
- Autosave — periodic background saves with configurable interval
- Neovim integration — restores file-backed tabs, windows, cwd, and cursor positions
- Startup restore — prompt or auto-restore when tmux starts
- Profiles — named restore configurations for different workflows
- Export/Import — portable snapshot and template bundles for sharing across machines
Restore is intentionally conservative — it does not try to blindly restart every process.
- tmux 3.0+
- bash 4.0+
- fzf — interactive picker UI
- jq — snapshot JSON processing
- yq — template YAML processing
- (optional) neovim — for nvim session restore
With TPM (recommended)
Add to your .tmux.conf:
set -g @plugin 'jwehrlich/tmux-revive'Then press prefix + I to install.
Clone the repo:
git clone https://github.com/jwehrlich/tmux-revive.git ~/.tmux/plugins/tmux-reviveAdd to your .tmux.conf:
run-shell ~/.tmux/plugins/tmux-revive/revive.tmuxReload tmux:
prefix + rAdd any of these to .tmux.conf before the plugin is loaded:
# Keybindings (defaults shown)
set -g @tmux-revive-save-key 'S'
set -g @tmux-revive-restore-key 'R'
set -g @tmux-revive-manage-key 'm'
# Shortcut carousel (seconds between rotations)
set -g @tmux-revive-shortcut-carousel-interval '10'
# Data directory (default: ~/.tmux/data or ~/.config/tmux/data)
# set -g @tmux-revive-data-dir '~/.tmux/data'
# Autosave
set -g @tmux-revive-autosave 'on'
set -g @tmux-revive-autosave-interval '900'
# Startup restore: 'prompt', 'auto', or 'off'
set -g @tmux-revive-startup-restore 'prompt'
# Snapshot retention
set -g @tmux-revive-retention-enabled 'on'
set -g @tmux-revive-retention-auto-count '20'
set -g @tmux-revive-retention-manual-count '60'
set -g @tmux-revive-retention-auto-age-days '14'
set -g @tmux-revive-retention-manual-age-days '90'| Path | Purpose |
|---|---|
~/.tmux/data |
State root (or ~/.config/tmux/data for XDG layouts) |
~/.tmux/data/snapshots/<hostname>/ |
Snapshots |
~/.tmux/data/templates/ |
Templates |
~/.tmux/data/runtime/ |
Runtime files |
Override the state root with @tmux-revive-data-dir in .tmux.conf or
TMUX_REVIVE_STATE_ROOT environment variable.
Migration note: If you have existing data in
~/.tmux/tmp/sessions, the plugin will automatically copy it to the new data directory on first load.
For deeper details, see:
Manual save:
prefix + SManual restore of the latest snapshot:
prefix + REnter manage mode:
prefix + mFrom manage mode:
m: open the fuzzy pickert: open tmuxchoose-treer: open the saved-session chooserb: browse saved snapshotsl: set the current session label0–9: jump to a pane shortcutC: start or stop the pane shortcut carousels: saveR: restore latest?: show the manage menuqorEscape: leave manage mode
The fuzzy picker now shows:
- current live session first
- other live sessions next
- saved sessions from the default snapshot source afterward
Saved rows in the picker are resume-only in the default view.
Press ? inside the picker to show the full cheat sheet. Quick reference:
| Key | Action |
|---|---|
Enter |
Jump to session/window/pane; action menu on snapshots/templates |
Esc |
Close picker |
Ctrl-b |
Toggle snapshots view |
Ctrl-e |
Toggle templates view |
Ctrl-a |
Restore all sessions from snapshot |
Ctrl-t |
Create new session |
Ctrl-r |
Rename session |
Ctrl-l |
Set session label |
Ctrl-d |
Delete session, snapshot, or template |
Ctrl-w |
Create new window |
Ctrl-p |
Create new pane |
Ctrl-g |
Set pane shortcut (0–9) |
? |
Show help cheat sheet |
Press Enter on a snapshot row (toggle snapshots with Ctrl-b first):
- Drill In — browse individual sessions inside the snapshot
- Restore — restore all sessions defined in that snapshot
- Export — export the snapshot as a portable
.tar.gzbundle - Delete — remove the snapshot directory (with confirmation)
- Convert to Template — create a YAML template from the snapshot
Press Enter on a template row (toggle templates with Ctrl-e first):
- Launch — apply the template (creates all sessions)
- Edit — open in
$EDITORwith validation on save - Delete — remove with confirmation
- Export — export the template as a portable
.tar.gzbundle - Rename — rename the template (updates both filename and
name:field) - Duplicate — copy to a new name
Assign numbered shortcuts (0–9) to jump directly to any pane. In the
picker, select a pane and press Ctrl-g to assign it to a slot. Then
use prefix m <number> (e.g. prefix m 3) to jump to that pane
instantly.
- Shortcuts are per-server — each tmux server instance has its own set
- Shortcuts persist across saves and restores via the snapshot manifest
- If a shortcut's target no longer exists, a brief warning is shown
- Use
prefix m Cor Rotate shortcuts from the manage menu to start or stop a rotating shortcut carousel - Set
@tmux-revive-shortcut-carousel-interval(seconds, default10) to control the rotation speed - Use
pane-shortcut.sh --listto see current shortcuts - Use
pane-shortcut.sh --clear <slot>to remove a shortcut
List saved sessions from the latest snapshot:
restore-state.sh --listDefault columns:
SESSION_GUIDSESSION_NAMELAST_UPDATED
Restore the latest snapshot:
restore-state.sh --yesPreview a restore plan without changing tmux:
restore-state.sh --preview
restore-state.sh --manifest /path/to/manifest.json --preview
restore-state.sh --session-name work --previewThe preview now includes advisory health warnings for issues such as:
- missing pane cwd paths
- missing
tail -ftargets - missing Neovim restore files
- snapshot host mismatch / legacy compatibility mode
Show the latest restore report explicitly:
show-restore-report.shThe restore report includes the same health warnings section so likely problems are visible after restore as well.
Inspect or apply snapshot retention manually:
prune-snapshots.sh --dry-run --print-actions
prune-snapshots.shRestore one saved session by label:
restore-state.sh --session-name work --yesRestore one saved session by GUID:
restore-state.sh --session-guid 123e4567-e89b-12d3-a456-426614174000 --yesRestore and attach from a normal shell:
restore-state.sh --session-name work --attach --yesResume one saved session with the convenience wrapper:
resume-session.sh --list
resume-session.sh work
resume-session.sh 123e4567-e89b-12d3-a456-426614174000The saved-session chooser now shows richer metadata for each saved session:
- snapshot timestamp
- snapshot reason
- short GUID
- first few window names
- whether that saved session is already live
Archived saved sessions are hidden from default choosers. To include them explicitly:
choose-saved-session.sh --manifest /path/to/manifest.json --include-archivedBrowse snapshots and pick an older one interactively:
choose-snapshot.sh --yesImported snapshots are hidden from the default browser view. To include them explicitly:
choose-snapshot.sh --yes --include-importedSnapshot browser keys:
Enter: choose a snapshot, then choose one saved session from itCtrl-a: restore all sessions from the selected snapshot- the right-side preview pane shows the restore plan for the highlighted snapshot
Export or import snapshot bundles:
# Export the latest snapshot
export-snapshot.sh --latest --output /tmp/latest-snapshot.tar.gz
# Export a specific snapshot by manifest path
export-snapshot.sh --manifest /path/to/manifest.json --output /tmp/work-snapshot.tar.gz
# Import a snapshot bundle
import-snapshot.sh --bundle /tmp/work-snapshot.tar.gzYou can also export/import snapshots from the picker:
- Press
Ctrl-bto toggle to the snapshots view - Press
Enteron any snapshot - Select Export from the action menu
The export creates a self-contained .tar.gz archive that can be shared and imported on another machine.
Imported snapshots:
- preserve original source metadata
- are tagged locally as imported
- are hidden from default choosers and startup flows unless you opt in explicitly
Archive or unarchive a saved session by GUID:
archive-session.sh --session-guid 123e4567-e89b-12d3-a456-426614174000
archive-session.sh --session-guid 123e4567-e89b-12d3-a456-426614174000 --unarchive
archive-session.sh --session-guid 123e4567-e89b-12d3-a456-426614174000 --statusArchived sessions:
- are stored in a durable
session-index.json, not in snapshot manifests - are hidden from startup prompts by default
- are hidden from default saved-session choosers and tmux-revive saved rows by default
Attach behavior:
- outside tmux,
--attachattaches only the current terminal window - inside tmux, restore switches only the current tmux client
- existing live sessions are skipped, never overwritten
- when a tmux client is available, restore also opens a post-restore report popup
Built-in help:
restore-state.sh --help
resume-session.sh --helpTemplates are intentionally authored workspace definitions written in YAML. Unlike snapshots (which capture live state and use a conservative command allowlist), templates trust all commands — every command: field runs unconditionally on apply.
Templates live at ~/.tmux/data/templates/<name>.yaml.
name: web-dev
description: Full-stack web development workspace
updated_at: "2026-03-26T00:00:00Z"
variables:
project_root:
prompt: "Project root directory"
default: ~/src/myproject
sessions:
- name: frontend
windows:
- name: editor
layout: main-vertical
panes:
- cwd: "{{project_root}}/frontend"
command: nvim .
- cwd: "{{project_root}}/frontend"
command: npm run dev
- name: shell
panes:
- cwd: "{{project_root}}/frontend"
- name: backend
windows:
- name: server
panes:
- cwd: "{{project_root}}/backend"
command: cargo run
- cwd: "{{project_root}}/backend"Supported pane fields: cwd, command, env (key-value map).
Layout can be any tmux built-in layout name (even-horizontal, even-vertical, main-horizontal, main-vertical, tiled).
Use bare ~ or ~/path for home-relative paths. Panes without cwd default to $HOME.
Templates can include host-specific overrides that merge on top of the base definition:
overrides:
my-work-laptop:
sessions:
- name: frontend
windows:
- name: editor
panes:
- cwd: /work/custom/pathOverrides match by session name → window name → pane index (positional). Only specified fields are overwritten; everything else inherits from the base.
Preview what would be created:
apply-template.sh --name web-dev --dry-runApply the template (creates sessions immediately):
apply-template.sh --name web-devApply and attach to the first session:
apply-template.sh --name web-dev --attachApply with variable overrides (skips interactive prompts for those variables):
apply-template.sh --name web-dev --var project_root=~/work/client
apply-template.sh --name web-dev --var project_root=~/work --var branch=developApply non-interactively (uses all defaults, no prompts):
apply-template.sh --name web-dev --no-promptIf a session name already exists, it is automatically renamed with a -2 suffix (incrementing: -3, -4, etc.).
When applying a template, session names that collide with existing live tmux sessions are automatically suffixed:
frontend→frontend-2(first collision)frontend-2→frontend-3(already suffixed — increment)frontend-99→frontend-100
This happens transparently. The restore log and dry-run output show the final names used. Collision handling applies to both apply-template.sh and restore-state.sh (template mode).
Templates can declare variables with prompts and default values. When applying, users are prompted for each variable (or defaults are used with --no-prompt).
variables:
project_dir:
prompt: "Project directory"
default: ~/src/myproject
branch:
prompt: "Git branch to checkout"
default: mainUse {{variable_name}} in cwd and command fields:
panes:
- cwd: "{{project_dir}}"
command: "git checkout {{branch}} && nvim ."Variables are expanded after ~ and $USER/$TMUX_REVIVE_TPL_* expansion. Unexpanded {{...}} placeholders cause an error.
From the picker (Ctrl-b to view snapshots), press Enter on a snapshot to get the action menu. Select Convert to Template to create a YAML template from the snapshot. You'll be prompted for a template name and optionally offered to open it for editing (to replace raw layout strings with named layouts).
See Snapshot action menu for all available snapshot actions.
From the command line:
template-create.sh --name from-snap --from-snapshot /path/to/manifest.jsonCheck a template for structural errors before applying:
template-validate.sh --name web-dev
template-validate.sh --file /path/to/template.yaml
template-validate.sh --name web-dev --quietValidation checks: YAML parseability, required fields (name, sessions, window name, non-empty panes), variable schema (each must have prompt), and warns on nonexistent cwd paths and undefined {{var}} references.
Create a blank scaffold template (with examples and comments):
template-create.sh --name my-workspace --blank
template-create.sh --name my-workspace --blank --edit # opens in $EDITORCreate a template from a saved snapshot:
template-create.sh --name from-snap --from-snapshot /path/to/manifest.jsonSave the current live session as a template:
template-save.sh --name my-workspace
template-save.sh --name fullstack --sessions frontend,backend
template-save.sh --name dev --description "Dev environment"Or edit templates directly as plain YAML:
$EDITOR ~/.tmux/data/templates/my-workspace.yamltemplate-list.sh
template-list.sh --jsonOpen a template in $EDITOR with validation on save:
template-edit.sh --name web-devThe editor re-opens if validation fails, letting you fix errors. The updated_at field is automatically updated on successful edits. If no changes are made, the script exits cleanly.
template-delete.sh --name old-workspace # prompts for confirmation
template-delete.sh --name old-workspace --yes # skip confirmationTemplates are integrated into the picker (prefix+m). Press Ctrl-e to toggle the templates view.
When you select a template and press Enter, an action menu appears:
- Launch — apply the template (creates all sessions)
- Edit — open in
$EDITORwith validation on save - Delete — remove with confirmation
- Export — export as a portable
.tar.gzbundle - Rename — rename the template (updates filename and
name:field) - Duplicate — copy the template to a new name
Press Ctrl-d on a template row to delete it directly (with confirmation).
Press Esc on the action menu to go back to the picker. See tmux-revive keybindings for the full reference.
To show templates on picker launch:
pick.sh --show-templatesThe preview pane shows the full YAML content when a template row is selected.
Panes can set environment variables that are passed to the command:
panes:
- cwd: ~/src/app
command: npm run dev
env:
NODE_ENV: development
PORT: "3000"Template cwd fields support $USER and any $TMUX_REVIVE_TPL_* environment variable for portability across machines:
panes:
- cwd: /home/$USER/projects
- cwd: $TMUX_REVIVE_TPL_PROJECTOnly $USER and variables with the TMUX_REVIVE_TPL_ prefix are expanded (to avoid accidentally leaking sensitive vars like $AWS_SECRET_KEY).
Export a template as a portable .tar.gz bundle:
template-export.sh --name web-dev
template-export.sh --name web-dev --output ~/shared/web-dev.tar.gzImport a template from a bundle:
template-import.sh web-dev.tmux-template.tar.gz
template-import.sh web-dev.tar.gz --name my-copy # rename on import
template-import.sh web-dev.tar.gz --force # overwrite existingRestore profiles live in profiles/ and are JSON files.
Built-in profiles:
safeall
Current v1 profile knobs:
attachpreviewinclude_archivedstartup_mode
Precedence:
- explicit CLI flags
- selected profile
- tmux global options
- built-in defaults
Use a profile directly:
restore-state.sh --profile safe --session-name work --yes
restore-state.sh --profile all --session-name work --yes
resume-session.sh --profile all work
choose-snapshot.sh --profile safe --yes
choose-saved-session.sh --manifest /path/to/manifest.json --profile allSet a default profile for tmux-driven startup and chooser flows:
set -g @tmux-revive-default-profile safeUseful overrides:
restore-state.sh --profile safe --no-preview --session-name work --yes
choose-saved-session.sh --profile all --hide-archived --manifest /path/to/manifest.jsonStartup restore is controlled by the tmux option:
set -g @tmux-revive-startup-restore promptSupported modes:
promptautooff
If a default restore profile is configured and includes startup_mode, that profile value overrides the tmux startup option.
Recommended default:
- use
promptunless you explicitly want automatic restore on attach
Behavior notes:
- startup restore is conservative and skips live-session collisions
- in
promptmode without a real client TTY, the prompt can still appear later on a real attach - the same prompt flow is also used when a new tmux session is created and saved sessions are restorable
- prompt actions now support both attach and no-attach flows:
- restore all and attach
- restore all without attaching
- choose one session and attach
- choose one session without attaching
- dismiss
- if a new blank session was created only to reach the prompt flow, choosing an attach action replaces that transient session instead of leaving it behind
- startup
automode also reuses the same restore report popup when a client tty is available
Each saved session has:
session_guid: durable identity used for reliable restore targetingsession_name: human-readable label shown to the user
At runtime, tmux may also use a distinct live tmux session name when needed for uniqueness.
Practical rule:
- use the label for readability
- use the GUID when you want an exact selector
Set or update the current session label:
prefix + m
lor:
set-session-label.shNon-Neovim panes restore conservatively:
- approved restart commands auto-run quietly
- exact unapproved running commands are preloaded at the prompt
- panes with no exact captured running command restore transcript context only
- panes remain visually quiet; tmux-revive does not inject explanatory banners into pane output
Current transcript behavior:
- snapshots replay the last
500captured pane lines
Current shell behavior:
- restored
zshandbashpanes load normal shared shell history - tmux-revive does not maintain per-pane shell history stores
Neovim panes:
- relaunch
nvim - restore file-backed tabs/windows, cwd, current tab/window, and cursor positions for supported clean sessions
- do not restore non-file buffers, terminal buffers, quickfix/location lists, or dirty buffers
tmux-revive ships a Lua module that enables full nvim session persistence:
- Add to your
init.lua(before anyrequire("core.send_to_nvim")):
-- Add tmux-revive's nvim integration to runtimepath
local tmux_revive_nvim = vim.fn.expand("~/.tmux/plugins/tmux-revive/nvim")
if vim.fn.isdirectory(tmux_revive_nvim) == 1 then
vim.opt.rtp:prepend(tmux_revive_nvim)
end
require("core.send_to_nvim").setup()- What it does:
| Feature | Description |
|---|---|
| tmux-revive save/restore | Snapshots nvim state (tabs, splits, cursors) during prefix + S; restores on prefix + R |
| Periodic auto-save | Writes session state every 60s to ~/.local/state/nvim/sessions/ |
| Crash recovery | If nvim crashes, reopening in the same tmux pane auto-restores state |
| Cwd-based fallback | Opening nvim in a directory with a saved session auto-restores it |
| Multiple instances | Each instance is tracked by PID; sessions never collide |
| Cleanup | Stale sessions (dead PID + >7 days for cwd) are swept on startup |
-
Restore priority chain (checked in order on startup):
TMUX_NVIM_RESTORE_STATEenv var set → tmux-revive is restoring, use that- Pane-keyed session exists + owning PID is dead → nvim crashed, auto-restore
- Cwd-keyed session exists + owning PID is dead → auto-restore
- None found → start fresh
-
Skipped when: nvim is opened with explicit file arguments (e.g.
nvim foo.lua) -
Commands:
:DiffRecovered— open diff views for dirty buffers that were recovered during restore
Approved auto-restart commands are intentionally conservative. Today that includes command families such as:
tail -f/tail -Fmake ...just ...npm run ...pnpm run ...yarn run ...uv run ...cargo run ...go run ...docker compose up ...python -m http.server
For exact current matching behavior, see state-common.sh.
Pane metadata helpers:
pane-meta.sh show
pane-meta.sh exclude-transcript on
pane-meta.sh set-command-preview 'npm run dev'
pane-meta.sh set-restart-command 'make -f /path/to/Makefile restart-proof'Hook options:
set -g @tmux-revive-pre-save-hook '...'
set -g @tmux-revive-post-save-hook '...'
set -g @tmux-revive-pre-restore-hook '...'
set -g @tmux-revive-post-restore-hook '...'Startup and autosave options:
set -g @tmux-revive-startup-restore prompt
set -g @tmux-revive-autosave on
set -g @tmux-revive-autosave-interval 900
set -g @tmux-revive-save-lock-timeout 120Shell-driven hook fallback example:
TMUX_REVIVE_PRE_RESTORE_HOOK='printf "%s\n" "$TMUX_REVIVE_HOOK_SELECTOR_NAME" >> /tmp/tmux-restore.log' \
restore-state.sh --session-name work --yesFor the full hook variable set and operational details, see workflow.md.
Latest restore log:
cat ~/.tmux/data/runtime/logs/latest-restore.logLatest restore report:
show-restore-report.shList saved sessions:
restore-state.sh --listRun the full regression suite:
tests/test_restore_stack.sh allRun a focused mixed restore scenario:
tests/test_restore_stack.sh mixed-sessionRun the template test suite:
bats tests/templates.batsWarning: Never run
tmux kill-serveror tmux-revive scripts against the default socket outside the test harness — this will kill your live session. The harness isolates each test on a dedicated socket via-L. For manual testing, usetmux -L test-socket.
If restore does not behave as expected:
- take a fresh save before retesting, especially after restore logic changes
- check the latest restore log
- verify whether the pane command is actually in the approved auto-restart set
- verify whether the saved cwd or target file still exists
tmux-revive checks for fzf, jq, and yq on startup and will error if any are missing. If yq is missing, template previews show a yellow warning instead of silently displaying blank content.
Important current limits:
- grouped tmux sessions are restored (leaders normally, followers linked via
new-session -t) - existing live sessions are skipped rather than overwritten
- restored windows intentionally keep
automatic-renamedisabled so saved names remain stable - Neovim restore is still limited to supported clean file-backed sessions; split-orientation fidelity remains future work
- a real day-to-day Neovim restore smoke pass is still useful after major restore changes
- old pre-GUID snapshots still restore in compatibility mode
See known-limitations.md for the full list.