title: “AI Tool Guide” description: “Technical guide and comparison.” date: 2026-05-19T00:00:00+08:00 lastmod: 2026-05-19T00:00:00+08:00 tech_stack: [] application_domain: Dev Utils source_version: ’' licensing_model: Open Source license_type: MIT file_size: ’' file_md5: ’' download_url: ’' backup_url: ’' last_maintained: “2026-05-19” draft: false categories: [“dev-utils”] tags: [“atuin”, “shell-history”, “cli-tools”, “sqlite”, “rust”, “sync”, “bash”, “zsh”, “fish”] aliases:

  • /posts/atuin/-

Atuin Shell History

Atuin replaces your existing shell history with a SQLite database and a full-screen fuzzy search UI activated via Ctrl+R.

Atuin Stats

The ```atuin stats```` command shows your most-used commands, total command count, and unique command breakdown.

Introduction

You have typed that complex kubectl command at least three times this week. You know it is in your history somewhere — probably buried under 40,000 other commands — but Ctrl+R reverse search cycles through matches one at a time, and grep ~/.bash_history returns a wall of noise. For developers who live in the terminal, shell history is a second memory. When it fails, productivity drops.

Atuin solves this with a SQLite-backed history database that records not just the command, but the context around it: exit code, working directory, hostname, session ID, and duration. With 29,794 GitHub stars and a Rust-based codebase, Atuin adds fuzzy search, encrypted cross-machine sync, and usage analytics to every shell session. This guide walks through a production-grade Atuin install, from first command to self-hosted sync server. Whether you need atuin install instructions for a single laptop or a fleet of developer workstations, the steps below are copy-paste ready.

Unlike traditional shell history that appends commands to a flat text file (~/.bash_history, ~/.zsh_history), Atuin stores each command as a structured record with 12+ fields. This enables queries impossible with grep: “show me all failed commands in /project/api run after 6 PM” or “what was that docker command I ran on the staging server last Tuesday?” The atuin tutorial below covers every step from install to daily workflow integration.

What Is Atuin?

Atuin is a shell history replacement tool written in Rust that stores commands in a local SQLite database with rich metadata, then optionally syncs that history across machines using end-to-end encryption. Released under the MIT license and maintained by the atuinsh organization, it supports Bash, Zsh, Fish, Nushell, Xonsh, and PowerShell (tier 2).

How Atuin Works

Atuin operates as a client-side history interceptor and optional sync client. Understanding its architecture helps when debugging sync issues or planning a self-hosted deployment.

Architecture Overview

+
* * *
+     preexec/precmd hooks     +
* * *
+
|   Shell     |  
* * *
>  |   Atuin Client   |
| (bash/zsh)  |                             |   (Rust binary)  |
+
* * *
+                             +
* * *
+
* * *
+
                                                     |
                                            +
* * *
v
* * *
+
                                            |   SQLite (local) |
                                            |   ~/.local/share |
                                            +
* * *
+
* * *
+
                                                     |
                              +
* * *
v
* * *
+
                              |              Sync Protocol V2                |
                              |   PASETO V4 (XChaCha20-Poly1305 + Blake2b) |
                              +
* * *
+
* * *
+
                                                     |
                              +
* * *
v
* * *
+
                              |         Atuin Server (self-hosted or cloud)  |
                              |         PostgreSQL or SQLite backend         |
                              +
* * *
+

Key Components

  1. Shell Hook Layer: Atuin registers preexec (before command) and precmd (after command) hooks via shell-specific plugins. These capture the command string, working directory, start time, and exit code.
  2. Local SQLite Database: All history stores in ~/.local/share/atuin/history.db using SQLite with WAL mode for concurrent read/write performance.
  3. Sync Client: Optional background sync pushes encrypted records to an Atuin server. Data is envelope-encrypted with per-record content encryption keys before leaving the machine.
  4. TUI Search Interface: A full-screen terminal UI (built with ratatui) replaces Ctrl+R with fuzzy/prefix/fulltext search and filter modes.

Encryption Details

| Protocol | Algorithm | Status | |


|


|


| | V1 (legacy) | XSalsa20Poly1305 (NaCl secretbox) | Phasing out | | V2 (current) | PASETO V4 Local (XChaCha20-Poly1305 + Blake2b) | Active |

V2 uses envelope encryption: each record gets a random CEK wrapped with the user’s master key. The master key lives at ~/.local/share/atuin/key and never leaves the device.

Why SQLite over Plain Text?

Traditional shell history stores commands as newline-delimited text. This works for history | grep but breaks down at scale: - Query performance: SQLite with proper indexes can search 500,000 commands in under 50ms. Grep on a 50MB text file takes 200ms+ and blocks the shell.

  • Structured metadata: Plain text cannot store exit codes, directories, or durations without fragile parsing.
  • Concurrent access: SQLite WAL mode allows the shell to write history while Atuin’s TUI reads it, without file locks corrupting data.
  • Deduplication and pruning: SQL DELETE with WHERE clauses lets you surgically remove entries (e.g., all commands containing password) rather than editing a text file.

Installation & Setup

# Unix/macOS — interactive install with shell setup prompts
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh

# Non-interactive (CI, Dockerfiles)
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh -s -- --non-interactive

The installer places the binary at ~/.atuin/bin/atuin and adds shell integration to your rc file.

Package Managers

# Homebrew (macOS/Linux)
brew install atuin

# Cargo (requires Rust toolchain)
cargo install atuin

# Arch Linux
sudo pacman -S atuin

# NixOS / nix
nix-env -iA nixpkgs.atuin

# Debian/Ubuntu (from GitHub releases)
VERSION="18.16.1"
curl -LO "https://github.com/atuinsh/atuin/releases/download/v${VERSION}/atuin_${VERSION}_amd64.deb"
sudo dpkg -i "atuin_${VERSION}_amd64.deb"

# Windows (WinGet)
winget install -e Atuinsh.Atuin

Shell Integration

After installation, add Atuin to your shell’s rc file: `````bash

Bash — add to ~/.bashrc

eval “$(atuin init bash)”

Zsh — add to ~/.zshrc

eval “$(atuin init zsh)”

Fish — add to ~/.config/fish/config.fish

atuin init fish | source

Nushell — add to config.nu

atuin init nu | save ~/.config/nushell/atuin.nu source ~/.config/nushell/atuin.nu


Reload your shell or run ````exec $SHELL```` to activate.

### Import Existing History

`````bash
# Auto-detect shell and import
atuin import auto

# Or specify explicitly
atuin import bash
atuin import zsh
atuin import fish

# Check what was imported
atuin stats

Verify Installation

$ atuin --version
atuin 18.16.1

$ atuin doctor
Atuin Doctor
Checking for diagnostics
[] Atuin is compiled with sqlite support
[] Atuin is compiled with sync support
[] Atuin config directory exists

Core Configuration

Atuin’s config file lives at ~/.config/atuin/config.toml. Before diving into settings, here is what the search interface looks like in practice with different filter modes applied: Atuin Search UI

Atuin’s TUI showing the inline search window with fuzzy matching and directory-scoped results. Here is a production-hardened configuration: `````toml

~/.config/atuin/config.toml

[settings]

Search mode: prefix, fulltext, fuzzy, skim

search_mode = “fuzzy”

Filter mode: global, host, session, directory

filter_mode = “global”

UI style: compact, full

style = “compact”

Sync settings

auto_sync = true sync_frequency = “5m” sync_address = “https://api.atuin.sh

Don’t record sensitive commands (regex patterns)

history_filter = [ “^export.*KEY”, “^export.*SECRET”, “^export.*PASSWORD”, “^aws configure”, “^ssh-keygen”, ]

Working directory filter — don’t record in these paths

cwd_filter = [ “/tmp/secrets”, “~/.*cred”, ]

Enter accepts command; false = Tab to edit first

enter_accept = true

Show helpful hints in search UI

show_help = false

Number of results

inline_height = 20


The atuin setup process generates a default configuration, but production environments benefit from explicit tuning. The config file uses TOML format and supports hot-reloading for most settings.

### Key Config Options Explained

`````bash
# View current config value
atuin config get search_mode
# fuzzy

# View resolved (effective) value
atuin config get search_mode --resolved
# fuzzy

# Set a config value inline
atuin config set search_mode fulltext
atuin config set filter_mode directory

# Print full config
atuin config print

Search and Filter Modes

# Ctrl+R toggles between filter modes interactively
# Default filter modes: global -> host -> session -> directory

# Command-line search with filters
atuin search --exit 0 --after "yesterday 3pm" make
atuin search --before "2026-01-01" --cwd /project deploy
atuin search --exit 1 --session       # failed commands this session

# Delete matching entries
atuin search --delete "rm -rf /accident"

Starship Prompt

Starship works alongside Atuin without conflicts. Both hook into shell events independently: `````toml

~/.config/starship.toml — no special config needed

Atuin handles history; Starship handles the prompt

Just ensure Atuin init runs before Starship init in your rc file


`````bash
# ~/.zshrc — order matters
eval "$(atuin init zsh)"       # Atuin first
eval "$(starship init zsh)"    # Starship second

tmux

Atuin integrates cleanly with tmux sessions. Each tmux window gets its own session ID, enabling per-window history filtering: `````bash

~/.tmux.conf — bind a key to open Atuin search

bind-key r run-shell “tmux send-keys C-r”

Atuin automatically detects tmux sessions via environment variables

Filter by session: press Ctrl+R then toggle filter modes


### fzf

Some users pair Atuin with fzf for file fuzzy-finding while using Atuin for history: `````bash
# Keep fzf for files, Atuin for history
# Disable fzf history binding (in ~/.bashrc or ~/.zshrc)
export FZF_DEFAULT_COMMAND='fd --type f --hidden'
# Don't bind Ctrl+R — let Atuin handle it

# fzf for files
alias ff='fzf --preview "bat --style=numbers --color=always {}"'

# Atuin for history (bound to Ctrl+R automatically)

Nushell

Nushell integration requires explicit setup since Nushell uses a different config system: `````nushell

config.nu

source ~/.config/nushell/atuin.nu

Set environment variables

$env.ATUIN_NOBIND = true # if you want custom keybinds


### Docker / Dev Containers

`````dockerfile
# Dockerfile.dev
RUN curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh -s -- --non-interactive
COPY config.toml /root/.config/atuin/config.toml
RUN echo 'eval "$(atuin init bash)"' >> /root/.bashrc

Self-Hosted Sync Server

For teams or privacy-conscious users, Atuin’s sync server can be self-hosted with Docker. The atuin tutorial for self-hosting below uses Docker Compose with PostgreSQL, which handles concurrent writes better than SQLite at server scale.

Docker Compose Setup

# docker-compose.yml
version: "3"
services: atuin: restart: always
    image: ghcr.io/atuinsh/atuin:latest
    command: server start
    volumes: - ./config:/config
      - ./atuin-data:/atuin-data
    links: - postgresql
    ports: - "8888:8888"
    environment: ATUIN_HOST: "0.0.0.0"
      ATUIN_PORT: "8888"
      ATUIN_OPEN_REGISTRATION: "true"
      ATUIN_DB_URI: "postgres://atuin:change-me@postgresql/atuin"
      RUST_LOG: "info,atuin_server=debug"
    user: "1000:1000"

  postgresql: image: postgres:14
    restart: always
    volumes: - ./postgres-data:/var/lib/postgresql/data
    environment: POSTGRES_USER: atuin
      POSTGRES_PASSWORD: change-me
      POSTGRES_DB: atuin
    user: "1000:1000"

  # Optional: automated backups
  backup: image: prodrigestivill/postgres-backup-local
    restart: always
    volumes: - ./backups:/backups
    links: - postgresql
    environment: POSTGRES_HOST: postgresql
      POSTGRES_DB: atuin
      POSTGRES_USER: atuin
      POSTGRES_PASSWORD: change-me
      POSTGRES_EXTRA_OPTS: "-Z6 --schema=public --blobs"
      SCHEDULE: "@daily"
      BACKUP_KEEP_DAYS: "7"

Start the Server

# Create data directories
mkdir -p atuin-data postgres-data backups

# Start services
docker compose up -d

# Check health
curl http://localhost:8888/health
# {"status":"ok"}

Client Configuration for Self-Hosting

# ~/.config/atuin/config.toml
[settings]
sync_address = "http://your-server:8888"
auto_sync = true
sync_frequency = "5m"
# Register a new account on your self-hosted server
atuin register -u myuser -e [email protected] -p securepassword

# Or login on additional machines
atuin login -u myuser -p securepassword

# Get your encryption key (back this up securely)
atuin key
# c2e2d6e5a9b1c3f4d7e8a9b0c1d2e3f4...

# Trigger sync
atuin sync

Kubernetes Deployment

# atuin-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata: name: atuin-server
spec: replicas: 2
  selector: matchLabels: app: atuin
  template: metadata: labels: app: atuin
    spec: containers: - name: atuin
          image: ghcr.io/atuinsh/atuin:18.16.1
          command: ["atuin", "server", "start"]
          ports: - containerPort: 8888
          env: - name: ATUIN_HOST
              value: "0.0.0.0"
            - name: ATUIN_DB_URI
              valueFrom: secretKeyRef: name: atuin-db-secret
                  key: uri

* * *
apiVersion: v1
kind: Service
metadata: name: atuin-service
spec: selector: app: atuin
  ports: - port: 8888
      targetPort: 8888

Benchmarks / Real-World Use Cases

Performance Characteristics

Atuin’s Rust implementation and SQLite backend provide consistent performance across large history datasets. These numbers were measured on an AMD Ryzen 5 5600X with NVMe storage and 32GB RAM: | Metric | Value | Notes | |


|


|


| | History query (100K entries) | ~15ms | fuzzy search, cold cache | | History query (500K entries) | ~45ms | fuzzy search, warm cache | | History query (1M entries) | ~85ms | fuzzy search, warm cache | | Sync initial upload (50K cmds) | ~30s | depends on bandwidth | | Incremental sync | ~1-3s | typical daily delta | | Binary size | ~12MB | single static binary | | Memory footprint | ~15MB | resident while idle | | SQLite DB size | ~150MB per 100K commands | with full metadata | | Shell startup overhead | ~5-10ms | one-time per shell session |

Production Use Cases

  1. Multi-Device Development: A developer with a work laptop, personal desktop, and cloud VM keeps all shell history in sync. Running docker compose up on one machine is searchable from another.
  2. Team Knowledge Preservation: A DevOps team self-hosts Atuin to maintain a searchable audit trail of infrastructure commands across rotating on-call engineers.
  3. Remote Environment Recovery: Developers using ephemeral cloud workstations (Gitpod, Coder) sync history so workspace teardown does not erase command context.

Stats Command Output

$ atuin stats
[▮▮▮▮▮▮▮▮▮▮]  9,607 fg
[▮▮▮▮▮▮▮▮▮ ]  9,458 vim
[▮▮▮       ]  3,144 ag
[▮▮▮       ]  3,095 git status
[▮▮        ]  2,248 git diff
[]  1,787 curl
[]  1,571 jq
[]  1,357 rg
[]  1,348 cd
[]  1,322 git log
Total commands: 62,849
Unique commands: 26,908

Migrating from Other History Tools

If you are switching from mcfly, Hstr, or plain shell history, the migration path is straightforward: `````bash

Step 1: Install Atuin (your existing history stays untouched)

curl –proto ‘=https’ –tlsv1.2 -LsSf https://setup.atuin.sh | sh

Step 2: Import existing history into Atuin’s database

atuin import auto

Step 3: Initialize shell integration

eval “$(atuin init zsh)” # or bash/fish

Step 4: Verify — both systems coexist during transition

atuin stats history | wc -l # native history still works

Step 5: After a week of testing, disable native history in rc file: # echo ‘unset HISTFILE’ » ~/.zshrc


Atuin does not delete or interfere with your existing ````~/.bash_history```` or ````~/.zsh_history```` file. The original history remains intact as a fallback.

## Advanced Usage / Production Hardening

### History Privacy Filters

Prevent sensitive commands from entering the database: `````toml
# ~/.config/atuin/config.toml
[settings]
history_filter = [
    # AWS credentials
    "^aws configure",
    "^export AWS_SECRET_ACCESS_KEY",
    # Generic secrets
    "^export.*SECRET",
    "^export.*PASSWORD",
    "^export.*TOKEN",
    "^echo.*password",
    # SSH keys
    "^ssh-add",
    "^ssh-keygen",
    # Database connection strings
    "^psql.*://.*:",
    "^mysql.*-p",
]

Backing Up Your History

# SQLite backup (safe, no lock issues)
sqlite3 ~/.local/share/atuin/history.db ".backup '/backup/atuin-$(date +%Y%m%d).db'"

# Or copy with WAL checkpoint
cp ~/.local/share/atuin/history.db ~/backups/atuin-backup.db

# Automated daily backup via cron
0 2 * * * sqlite3 ~/.local/share/atuin/history.db ".backup '/backups/atuin/atuin-$(date +\%Y\%m\%d).db'" && find /backups/atuin -mtime +30 -delete

Monitoring Sync Health

# Check last sync time
atuin sync --force  # force a sync and show status

# View sync info
atuin info
# Atuin v18.16.1
# Sync interval: 5m
# Sync address: https://api.atuin.sh
# Last sync: 2026-05-20T08:15:32Z
# History count: 47,231

# Check for issues
atuin doctor

Theming the TUI

# ~/.config/atuin/config.toml
[theme]
# Use terminal's default colors
use_default_colors = true

# Or specify explicit colors
base = "#1e1e2e"
layer1 = "#313244"
text = "#cdd6f4"
accent = "#89b4fa"

Multi-Machine Key Migration

When setting up a new machine, transfer your encryption key securely: `````bash

On old machine — copy key to clipboard (or secure transfer)

cat ~/.local/share/atuin/key

On new machine — after install and login

mkdir -p ~/.local/share/atuin echo “YOUR_KEY_HERE” > ~/.local/share/atuin/key chmod 600 ~/.local/share/atuin/key

Verify sync works

atuin sync atuin stats


## Comparison with Alternatives

| Feature | Atuin | mcfly | fzf + history | Hstr |
|
* * *
|
* * *
|
* * *
|
* * *
|
* * *
|
| **Database** | SQLite | SQLite | Plain text file | Plain text file |
| **Cross-machine sync** | Yes (E2EE) | No | No | No |
| **Search UI** | Built-in TUI | Built-in TUI | fzf integration | Built-in TUI |
| **Context logging** | cwd, exit, duration, host | cwd, exit | None | None |
| **Statistics** | ````atuin stats```` | No | No | No |
| **Shell support** | bash, zsh, fish, nu, xonsh | bash, zsh, fish | Any shell | bash, zsh |
| **Encryption** | PASETO V4 | None | None | None |
| **Self-hosted server** | Yes (Docker/K8s) | N/A | N/A | N/A |
| **AI integration** | Yes (optional) | No | No | No |
| **History import** | bash, zsh, fish, nu | bash, zsh, fish | N/A | bash, zsh |
| **GitHub Stars** | 29,794 | 6,800 | 67,000 (fzf) | 3,900 |
| **Binary size** | ~12MB | ~4MB | ~4MB (fzf only) | ~2MB |

### When to Choose Each Tool

- **Atuin**: You want encrypted sync across machines, rich context metadata, and a full-featured search UI. Best for developers working on 2+ machines who value history analytics.
- **mcfly**: You want a lightweight, fully local tool with intelligent contextual ranking. Best for single-machine users who want zero network dependencies.
- **fzf + history**: You already use fzf for files and want a minimal history solution. Best for users who want one tool for everything fuzzy.
- **Hstr**: You want a simple, lightweight history TUI without database overhead. Best for minimalists on bash/zsh.

## Limitations / Honest Assessment

Atuin is not the right tool for every scenario: 1. **No Executor Tracking**: Atuin records the command but not whether it was typed manually, executed by a script, or generated by an AI coding assistant. All sources look identical in the database.

2. **Local Database is Unencrypted**: The SQLite database at ````~/.local/share/atuin/```` is stored in plaintext for performance. History sync is encrypted, but local storage is not. Use filesystem encryption (LUKS, FileVault) for protection.

3. **Bash Integration Can Be Fragile**: Bash's ````preexec```` hooks rely on DEBUG traps that can conflict with other tools (pyenv, nodenv, certain PROMPT_COMMAND setups). Zsh and Fish integrations are more reliable.

4. **Sync Requires Account**: Even self-hosted sync requires user registration. There is no anonymous or "just sync to S3" mode.

5. **Up-Arrow Binding Surprises New Users**: Atuin replaces the up-arrow key by default, which can confuse users who just want to cycle recent commands. Set ````filter_mode_shell_up_key = "session"```` or disable the binding entirely.

6. **PowerShell Support is Tier 2**: While functional, PowerShell integration receives less testing and may lag behind Unix shell features.

## Frequently Asked Questions

### How do I disable the up-arrow binding?

Add ``filter_mode_shell_up_key = "global"`` or ``show_preview = false`` in your config. To completely disable Atuin on up-arrow, add ``export ATUIN_NOBIND=1`` before the init line and manually bind only ``Ctrl+R``: `````bash
# ~/.bashrc
export ATUIN_NOBIND=1
eval "$(atuin init bash)"
bind '"\C-r": "\C-aatuin search\C-j"'

Can I use Atuin without any sync?

Yes. Atuin works fully as a local tool. Simply skip registration and ignore all sync commands. Your history stores in local SQLite and all search features work offline.

How is my data encrypted?

All sync data is encrypted client-side with PASETO V4 (XChaCha20-Poly1305 + Blake2b) before leaving your machine. The sync server (whether self-hosted or atuin.sh) only sees encrypted blobs — it cannot decrypt or read your commands. Your local SQLite database is not encrypted for search performance.

What happens if I lose my encryption key?

Your encryption key is required to decrypt synced history. If lost, you cannot recover previously synced data. The key is displayed during initial setup with atuin key — back it up in a password manager. Local history remains accessible regardless.

Can two users share a history database?

Not directly. Atuin’s sync model is designed for one user across multiple devices. For team shared history, each user maintains their own database and sync account. Self-hosted servers support multiple independent accounts.

Does Atuin slow down my shell?

No measurable impact. The Rust binary adds ~5-10ms to shell startup (one-time), and command capture happens asynchronously via shell hooks. The SQLite WAL mode ensures writes do not block the shell prompt.

How do I delete a command from history?

# Delete by search pattern
atuin search --delete "sensitive-command"

# Or use the TUI — find the command, then press Alt+Delete

Conclusion

Atuin turns shell history from a flat text file into a structured, searchable, and portable database. With 29,794 GitHub stars, end-to-end encrypted sync, and support for every major shell, it is a practical upgrade for any developer who lives in the terminal.

Next steps:

  1. Run curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh to install
  2. Import existing history with ````atuin import auto```
  3. Register for sync or configure a self-hosted server
  4. Join the Telegram developer community for tips and troubleshooting

Before you deploy any of the tools above into production, you’ll need solid infrastructure. Two options dibi8 actually uses and recommends: - DigitalOcean — $200 free credit for 60 days across 14+ global regions. The default option for indie devs running open-source AI tools.

  • HTStack — Hong Kong VPS with low-latency access from mainland China. This is the same IDC that hosts dibi8.com — battle-tested in production.

Affiliate links — they don"t cost you extra and they help keep dibi8.com running.

Sources & Further Reading

{ “@context”: “https://schema.org”, “@type”: “Article”, “headline”: “Atuin: 29,794 GitHub Stars — Shell History Sync Setup Guide 2026”, “datePublished”: “2026-05-19”, “dateModified”: “2026-05-19”, “author”: { “@type”: “Organization”, “name”: “Dibi8” }, “publisher”: { “@type”: “Organization”, “name”: “Dibi8”, “logo”: { “@type”: “ImageObject”, “url”: “https://dibi8.com/logo.png" } }, “mainEntityOfPage”: { “@type”: “WebPage”, “@id”: “https://dibi8.com/resources/atuin" } }



Found this helpful? Join our Telegram community for daily AI tool updates!