Sync and search shell history across machines with atuin

Install atuin, import your existing shell history, search it with Ctrl-R and sync it end-to-end encrypted between machines.

how-to8 steps

Updated

in short

Install atuin, add atuin init to your shell rc file, run atuin import auto, then atuin register on one machine and atuin login on the others. History is encrypted on your machine before it is uploaded.

atuin stores shell history in a local SQLite database and records the exit code, working directory, hostname, session and duration of each command. It binds Ctrl-R and the up arrow to a full-screen search, and can sync history between machines through a server that only ever sees encrypted data.

This guide covers a local setup first, then sync, key bindings and self-hosting. Sync is optional; everything up to step 5 works offline.

Tools used

Before you start

  • zsh, bash, fish, nushell, xonsh or PowerShell
  • For bash: ble.sh 0.4+ is recommended; bash-preexec also works with some limits
  • An email address and username if you want to use sync

Steps

  1. Step 1: Install atuin

    The official install script puts the binary in ~/.atuin/bin, sets up the shell plugin and walks you through import and sync interactively. Package managers install only the binary, so you also need step 2.

    sh
    curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh
    sh
    # or, with a package manager
    brew install atuin
    pacman -S atuin
    cargo install atuin --locked
  2. Step 2: Load the shell plugin

    Add the line for your shell to its rc file, then open a new terminal. atuin init installs the hooks that record each command and the key bindings.

    In bash, atuin 18.18.0 and newer loads a bundled copy of bash-preexec automatically if neither ble.sh nor bash-preexec is already loaded. ble.sh gives more accurate durations and exit codes.

    zsh
    # ~/.zshrc
    eval "$(atuin init zsh)"
    bash
    # ~/.bashrc
    eval "$(atuin init bash)"
    fish
    # ~/.config/fish/config.fish, inside the is-interactive block
    atuin init fish | source
  3. Step 3: Import your existing history

    atuin only records new commands through the plugin. atuin import auto reads your current shell's history file, detected from $SHELL. Name the shell explicitly if detection picks the wrong one, and set HISTFILE to read a non-default file.

    Imported entries lack exit codes and durations because the old file never stored them. Your old history file is not replaced and keeps being written by the shell.

    sh
    atuin import auto
    sh
    atuin import zsh
    HISTFILE=~/backup/.zsh_history atuin import zsh
  4. Step 4: Search with Ctrl-R and the up arrow

    Press Ctrl-R and type part of a command. Inside the search UI, Ctrl-R cycles the filter mode, such as global, host, session or directory, Enter runs the selected command, Tab puts it on the prompt for editing, and Alt-1 to Alt-9 pick an entry by its number.

    atuin search runs the same queries non-interactively, with filters such as exit code and time.

    sh
    # successful make commands run after 3pm yesterday
    atuin search --exit 0 --after "yesterday 3pm" make
  5. Step 5: Change or disable the up-arrow binding

    By default the up arrow also opens atuin. To keep Ctrl-R but give the up arrow back to your shell, pass --disable-up-arrow to atuin init. --disable-ctrl-r does the reverse, and setting ATUIN_NOBIND before atuin init binds nothing so you can bind keys yourself.

    If you want to keep the up arrow but scope it, set filter_mode_shell_up_key_binding in ~/.config/atuin/config.toml; for example directory shows only commands run in the current directory.

    zsh
    # ~/.zshrc: Ctrl-R only
    eval "$(atuin init zsh --disable-up-arrow)"
    zsh
    # ~/.zshrc: bind keys yourself
    export ATUIN_NOBIND="true"
    eval "$(atuin init zsh)"
    bindkey '^r' atuin-search
    toml
    # ~/.config/atuin/config.toml
    filter_mode_shell_up_key_binding = "directory"
  6. Step 6: Register and run the first sync

    On your first machine, register an account. Leave out -p and atuin prompts for the password, which keeps it out of your history. Registration generates an encryption key and stores it locally; print it with atuin key and save it in a password manager. Nobody can recover it for you.

    After that, sync runs automatically in the background. Run atuin sync to trigger it by hand.

    sh
    atuin register -u <USERNAME> -e <EMAIL>
    atuin key
    atuin sync
  7. Step 7: Log in on your other machines

    Install atuin and the shell plugin on the next machine, optionally import its local history, then log in. atuin asks for your password and the key from atuin key. If entries seem to be missing after the first sync, atuin sync -f forces a full sync.

    sh
    atuin import auto
    atuin login -u <USERNAME>
    atuin sync
  8. Step 8: Optional: use a self-hosted server

    The server is a separate binary, atuin-server, backed by PostgreSQL, MySQL or SQLite. It reads ~/.config/atuin/server.toml; db_uri is required and open_registration defaults to false, so set it to true while you create accounts. The project recommends tracking tagged releases rather than main or latest.

    On each client, set sync_address to your server before you run atuin register or atuin login. The ATUIN_SYNC_ADDRESS environment variable works too.

    toml
    # server: ~/.config/atuin/server.toml
    host = "0.0.0.0"
    port = 8888
    open_registration = true
    db_uri = "postgres://user:password@hostname/database"
    sh
    atuin-server start
    toml
    # client: ~/.config/atuin/config.toml
    sync_address = "https://atuin.example.com"

Privacy and encryption

  • History is encrypted on the client before upload; the server, hosted or self-hosted, stores only ciphertext.
  • The key lives in the atuin data directory (~/.local/share/atuin/key on Linux and macOS). Losing it means losing access to synced data.
  • secrets_filter is on by default and skips commands that match AWS, GitHub, GitLab, Slack, Stripe and similar token patterns.
  • history_filter and cwd_filter in config.toml take regexes to exclude commands or directories; a leading space also skips a command.
  • Without sync, the only network call is a version check to api.atuin.sh, at most once an hour. Set update_check = false to stop it.
  • atuin account delete removes your account and synced history from the server. Local data is kept.

Troubleshooting

  • Ctrl-R still shows the old search: the rc file was not reloaded. Open a new terminal or run exec $SHELL.
  • Another tool such as fzf also binds Ctrl-R: the binding set last in your rc file wins. Put the atuin init line after it.
  • Commands from an IDE terminal are not recorded: the IDE starts a non-interactive shell. Configure it to run an interactive one, such as /bin/bash -i.
  • Durations or exit codes look wrong in bash: that is a bash-preexec limit. Load ble.sh 0.4+ before atuin init bash.
  • Another machine is missing history after login: run atuin sync -f for a full sync.
  • Typing ? opens Atuin AI: recent releases bind it by default. Add --disable-ai to the atuin init line.

Also worth a look

  • fzfFuzzy Ctrl-R over your shell's existing history file; no database and no sync.

FAQ

Do I have to use sync?
No. Without `atuin register` or `atuin login`, history stays in the local SQLite database and nothing is uploaded.
Does atuin replace my shell's history file?
No. The shell keeps writing its own history file; atuin records to its own database alongside it.
I forgot my sync password. Can I reset it?
There is no password reset. The project's FAQ says that as long as you are still logged in on at least one machine, it is safe to delete the account with `atuin account delete` and register again.

related