The Best Zsh History Replacement in 2026 (and How to Upgrade)
Fix native Zsh history settings and search first, then compare replacements and install, import, search, and roll back a database-backed history tool.
When Zsh history lets you down, it is usually one of two different problems. Either the command was never saved (or the file was damaged), or it is saved but you cannot find it quickly. A replacement tool can help with the second problem, but the first is often a few lines of Zsh configuration. This guide shows how to check and fix native Zsh history first, how to search it well, where a flat history file really runs out, and how to install, import into, and roll back a database-backed replacement.
Review note: I build Suvadu, one of the tools discussed here. I reviewed this guide on 19 September 2026 against the Zsh 5.9 manual (zshoptions, zshbuiltins, zshzle) and Suvadu 0.4.1.
What built-in Zsh history already does
Zsh keeps an in-memory history list and, if HISTFILE is set, saves it to a text file such as ~/.zsh_history. A handful of parameters and options control it:
HISTFILEis where history is saved. If it is unset, history lives only in memory and disappears when the shell exits. macOS sets it in/etc/zshrc; on other systems it depends on your distribution or framework.HISTSIZEis how many entries each shell keeps in memory;SAVEHISTis how many are written to the file. macOS defaults are 2,000 and 1,000. Older commands are dropped once these limits are reached, so raise them if you want long history, keepingHISTSIZEat least as large asSAVEHIST.INC_APPEND_HISTORYwrites each command to the file as soon as it is entered instead of when the shell exits, so a crashed or killed terminal does not lose its session.INC_APPEND_HISTORY_TIMEwrites after the command finishes, which records durations correctly.SHARE_HISTORYappends commands as they are entered and also imports new commands from other running shells. The manual treats it and the two incremental-append options as alternatives: set one, not all three.EXTENDED_HISTORYsaves each command's start time and duration in the file (: 1758268800:3;make test).HIST_IGNORE_DUPSskips a command identical to the previous one;HIST_IGNORE_ALL_DUPSremoves older duplicates;HIST_IGNORE_SPACEdrops commands typed with a leading space, which is useful for commands you do not want saved.
A reasonable starting point in ~/.zshrc:
HISTFILE=~/.zsh_history
HISTSIZE=100000
SAVEHIST=100000
setopt EXTENDED_HISTORY
setopt SHARE_HISTORY # or INC_APPEND_HISTORY / INC_APPEND_HISTORY_TIME, not together
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE Missing or damaged history vs. hard-to-find history
Work out which problem you have before changing tools:
- Commands never reach the file. Check
echo $HISTFILE $HISTSIZE $SAVEHISTin the affected shell. An unsetHISTFILE, a tinySAVEHIST, or a framework overriding your values after your settings will all lose history. Without an incremental-append or sharing option, a terminal that is killed rather than exited never writes its commands. - Other terminals do not see new commands. That is expected without
SHARE_HISTORY: each shell loads the file at startup. Runfc -RIto read new entries from the file on demand, or enable sharing. - "zsh: corrupt history file". The file contains a damaged or truncated entry, often after a crash or a full disk. Back up the file first, close other shells so they do not rewrite it, and remove the damaged lines in an editor. A history replacement does not repair this file for you.
- The command is saved but you cannot find it. That is a recall problem. Try the native search tools below before deciding it is a tooling problem.
Search native Zsh history before you replace it
Native Zsh search is more capable than its reputation:
- Ctrl+R runs
history-incremental-search-backwardin the default emacs keymap. Keep pressing it to step to older matches. It is not bound in the vi keymaps, so bind it yourself if you use vi mode.history-incremental-pattern-search-backwardaccepts glob patterns. fc -llists history;fc -l 1lists all of it, and piping togrepgives you every match at once:fc -l 1 | grep docker.history -E(or-ifor ISO format) shows when each command ran, which needsEXTENDED_HISTORYfor commands from earlier sessions.- zsh-history-substring-search makes Up/Down cycle through commands containing what you have typed, similar to the Fish shell.
Where a history file runs out
With good settings, native history is reliable for many people. A flat file still has limits that configuration cannot fix:
- Little context. Even with
EXTENDED_HISTORY, the file stores a start time, duration, and command. It does not record the directory, the exit status, or which terminal session ran a command, so you cannot ask "what failed in this project yesterday?" - One match at a time. Ctrl+R shows a single match; to see and filter many results you drop to
fc -l | grepor add a tool such as fzf. - Size limits are a trade-off. You can raise
SAVEHISTa lot, but the file is rewritten to trim it and every shell reads it at startup. - No view of AI agents. Coding agents usually run commands in non-interactive shells, which do not write to your history file, so their commands rarely appear there.
Your options in 2026
| Tool | What it is | Best for |
|---|---|---|
| Native Zsh + plugins | The settings above, plus zsh-history-substring-search | Keeping your setup small once recording is fixed |
| fzf | A fuzzy finder bound to Ctrl+R over your existing history | A quick, zero-lock-in search upgrade |
| McFly | Neural-ranked Ctrl+R with a SQLite store alongside the history file | Context-ranked recall |
| Atuin | SQLite history with fuzzy scoped search, many importers, optional encrypted sync, agent hooks, and MCP | The strongest all-round shell-history replacement, Fish or multi-machine users |
| Suvadu | Local SQLite history for Bash and Zsh plus AI-agent session capture | Reviewing and continuing coding-agent work alongside your own history |
fzf and McFly still read or keep your normal history file, so fix recording first. Atuin and Suvadu record new commands through their own shell hooks. For a fuller comparison, see Atuin, fzf, McFly, hstr, and Suvadu compared.
Switching to Suvadu: install, import, search, and roll back
Suvadu records each new command in a local SQLite database with its directory, exit code, duration, session, and whether a person or an agent ran it. It does not change HISTFILE or your history options: Zsh keeps writing ~/.zsh_history as before. Like HIST_IGNORE_SPACE, it skips commands typed with a leading space. It requires Zsh 5.1+ on macOS or Linux.
Know two 0.4.1 limits before you start: import accepts only Zsh history and Suvadu's own JSONL, and typed Ctrl+R search only matches within the newest ~5,000 eligible entries (a date or directory filter reaches older ones; a fix is planned but not released).
1. Install
brew tap AppachiTech/suvadu && brew install suvadu Alternatives: curl -fsSL https://downloads.appachi.tech/suvadu/install.sh | bash (verifies a checksum and installs to /usr/local/bin with sudo) or cargo install suvadu. None of them edit your shell configuration.
2. Enable the Zsh hook
Add this line once to ~/.zshrc, then reload. If another tool such as Atuin, McFly, or fzf binds Ctrl+R or the arrow keys, disable its binding first; the one loaded last wins.
echo 'eval "$(suv init zsh)"' >> ~/.zshrc
source ~/.zshrc
suv doctor The hook binds Ctrl+R to Suvadu search and Up/Down to Suvadu's recency-first recall. To keep native (or zsh-history-substring-search) arrow behavior, set enable_arrow_navigation = false under [shell] in Suvadu's config or in suv settings, then open a new shell.
3. Import your existing Zsh history
Preview first, then import. The import reads the file and never modifies it, and reports how many commands it imported and skipped.
suv import --from zsh-history ~/.zsh_history --dry-run
suv import --from zsh-history ~/.zsh_history If the file used EXTENDED_HISTORY, imported commands keep their timestamps and durations, and importing the same file again skips entries already present. Plain-format lines have no timestamps, so they are stamped with the import time and are not deduplicated on a repeat import; import a plain file once. The file never held directories or exit codes, so imported entries do not have them. Multi-line commands are kept together. If the file contains invalid UTF-8 bytes, Suvadu warns and replaces them.
4. Run a first search
echo hello Press Ctrl+R and type hello. The detail panel shows the directory, exit code, and time of the command you just ran. Select it to put it on your prompt; press Enter to run it. Search is literal: every typed word must appear in the command.
Next step: if nothing was recorded or Ctrl+R still opens another tool, work through the troubleshooting guide. The installation guide and shell integration reference cover Zsh setup in more detail.
5. Roll back
- Stop recording without removing anything:
suv pausepauses the current shell;suv disablestops recording everywhere untilsuv enable. The key bindings stay active. - Return to native Zsh history: delete the
eval "$(suv init zsh)"line from~/.zshrcand open a new terminal (or runexec zsh). Re-sourcing the file in the same shell does not remove bindings that are already loaded. - Uninstall: for Homebrew and Cargo installs,
suv uninstallasks for confirmation, removes the binary, removes that exactevalline from~/.zshrcand~/.bashrc, and removes Suvadu's Claude Code and Codex hooks. For an install-script install it only prints the binary's path for you to delete (for examplesudo rm /usr/local/bin/suv), so remove the~/.zshrcline yourself. See update and uninstall.
Uninstalling keeps Suvadu's database and config so you can change your mind; delete them separately if you want them gone. Your ~/.zsh_history is untouched throughout, and because Zsh kept writing it while Suvadu was active, it still contains the commands from that period, as far as your Zsh settings saved them.
The bottom line
Start with native Zsh: set HISTFILE, generous HISTSIZE/SAVEHIST, EXTENDED_HISTORY, and one of SHARE_HISTORY or an incremental-append option. That fixes most missing history. If you then want richer recall and context, fzf and McFly are small steps and Atuin is the most complete shell-history replacement. Suvadu is worth trying when you also want to see and continue what your coding agents did.
Builder of Suvadu. Writes Rust, thinks about shell history more than most people, and believes developer tools should be local-first.