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 replaces your existing shell history with a SQLite database and a full-screen fuzzy search UI activated via Ctrl+R.

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
- Shell Hook Layer: Atuin registers
preexec(before command) andprecmd(after command) hooks via shell-specific plugins. These capture the command string, working directory, start time, and exit code. - Local SQLite Database: All history stores in
~/.local/share/atuin/history.dbusing SQLite with WAL mode for concurrent read/write performance. - 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.
- TUI Search Interface: A full-screen terminal UI (built with
ratatui) replacesCtrl+Rwith 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
DELETEwithWHEREclauses lets you surgically remove entries (e.g., all commands containingpassword) rather than editing a text file.
Installation & Setup
One-Line Install (Recommended)
# 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’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"
Integration with Popular Tools
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
- Multi-Device Development: A developer with a work laptop, personal desktop, and cloud VM keeps all shell history in sync. Running
docker compose upon one machine is searchable from another. - Team Knowledge Preservation: A DevOps team self-hosts Atuin to maintain a searchable audit trail of infrastructure commands across rotating on-call engineers.
- 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:
- Run
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | shto install - Import existing history with ````atuin import auto```
- Register for sync or configure a self-hosted server
- Join the Telegram developer community for tips and troubleshooting
Recommended Hosting & Infrastructure
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
- Atuin GitHub Repository
- Atuin Official Documentation
- Atuin Self-Hosting Guide
- Atuin Configuration Reference
- PASETO V4 Specification
- Ellie Huxtable — Atuin Creator Blog
- mcfly GitHub Repository
- fzf GitHub Repository
- Hstr GitHub Repository
{ “@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" } }
Related Articles
Found this helpful? Join our Telegram community for daily AI tool updates!