Atuin - The Magical Shell Tool

In part two of my series, "Learning on Leave" I spent some time going through the docs for Atuin. I even went down a rabbit hole of rolling my own sync server!

What is Atuin and why would I use it?

Atuin is "Shell history supercharged". Per their website:

Sync, search, and back up your shell history with end-to-end encryption. The magical shell tool loved by developers worldwide.

I first learned about it on Mastodon from b0rk. As an aside, she's also where I first learned about fish which is a great shell!

It's been a real game changer for me. Logging into different servers and having access to my shell history across all machines is kind of a game changer.

Things I learned

  1. ctrl-r

When I migrated from just the standard zsh history to Atuin, I was just used to using ctrl-r to search, but Atuin offered use of the up-arrow key to browse through my history. This seemed easier to me, so I started to use that exclusively and then completely forgot that ctrl-r ever existed 🤦🏻‍♂️

The thing is, ctrl-r will also allow you to cycle through the filter modes (global, host, session, directory, workspace, session-preload). This allows you to see all commands, or focus on just the session history. You can update the settings to have the history set to a specific filter mode. The default is global which means all of the synced history from all servers. After learning about this I decided to update mine to host.

See the docs for more on filter mode.

See the docs for more on workspaces

  1. ctrl-o

While you're in the history (using ctrl-r) you can use ctrl-o to show you the history of that command. The history is limited by what your filter mode is. If you set it to host it will only show the history of that command on the host you're currently on

  1. Agent integration

There is agent integration via atuin hook install claude-code and/or atuin mcp install claude-code.

Using atuin hook install claude-code allows Atuin to capture commands that your agents are running. Atuin tags these so they are easy to search on or filter out.

If you want to use atuin mcp install claude-code you will need to have your sync server set to Atuin's sync server, or, if you decided to roll your own sync server, you'll need to set up a self hosted agent server too.

See the docs for more details.

See the docs for more details.

  1. Deduplicating your history

There is a command that will dedup your history to clean it up if needed. Why would you want to do this if the point of Atuin is to have all of your history? It can be super helpful after you've imported your history, or if a runaway script polluted your history. There are other use cases too I'm sure. But just knowing it's there in case it's needed is pretty nice.

An example would be

atuin history dedup --dry-run --before "2025-01-01" --dupkeep 1

This would perform a dry run of what would be removed for anything from before January 1, 2025. It would only keep 1 copy (the most recent) in history.

To perform the deduplication

atuin history dedup --before "2025-01-01" --dupkeep 1

See the docs for more details.

  1. Themes

Like many (all) developer tools, we have an ability to express ourselves through themes. The default, "default" is fine, but I prefer Dracula styled themes. There isn't a library of themes, but there are good docs on how to create your own theme.

My Dracula theme looks like this:

[theme]
name = "dracula"

[colors]
Base = "#f8f8f2"          # Dracula Foreground
Muted = "#6272a4"         # Dracula Comment/Grey
Title = "#bd93f9"         # Dracula Purple
Important = "#ff79c6"     # Dracula Pink
Guidance = "#8be9fd"      # Dracula Cyan
Annotation = "#6272a4"    # Dracula Comment/Grey
AlertInfo = "#50fa7b"     # Dracula Green
AlertWarn = "#ffb86c"     # Dracula Orange
AlertError = "#ff5555"    # Dracula Red

And is placed in ~/.config/atuin/themes/dracula.toml.

I then updated my config.toml with this block

[theme]
name = "dracula"
  1. atuin info

The command atuin info shows the location of config files on your system. When I run it on my local macBook Pro I get

Config files:
client config: "/Users/ryan/.config/atuin/config.toml"
server config: "/Users/ryan/.atuin/server.toml"
client db path: /Users/ryan/.local/share/atuin/history.db
key path: /Users/ryan/.local/share/atuin/key
meta db path: /Users/ryan/.local/share/atuin/meta.db

Env Vars:
ATUIN_HOME = None
ATUIN_CONFIG_DIR = None

Version info:
version: 18.24.3
commit:  4532ceacd860885c2310ab8fab92a1bc00d6d7e8

This command can be especially helpful when you're trying to figure out why your config.toml isn't applying your settings the way you expect. In my case, the default one from the atuin install was being used instead of my user one. To fix it I deleted the one set up by atuin. If there are multiple config.toml files you do get a warning when running atuin info so you can see that there is something amiss.

See the docs for more details.

  1. atuin output search

atuin output search allows for cli search capabilities for the output from commands. The command will return the output in addition to the command that prompted the output.

Per the docs:

Matches come back most relevant first, each with its command, when and where it ran, and its output.

you do need to ensure that output capture is enabled.

If it's not, run

atuin config enable output-capture

See the docs for full details.

  1. Search

The highlight feature of Atuin is its search capabilities and yeah, there's a lot of power in the search.

You can interactively search using a TUI with atuin search -i. This isn't any different than ctrl-r or the up-arrow. BUT if you combine it with a search term, like atuin search -i docker you'll immediately be searching only your docker commands in your history.

There are also some great examples of what's available here

  1. atuin stats

You can run atuin stats to get details on what unique commands you've been running.

For example

atuin stats week --filter-mode session

returns

[▮▮▮▮▮▮▮▮▮▮] 4 cat
[▮▮▮▮▮▮▮   ] 3 clear
[▮▮▮▮▮▮▮   ] 3 grep
[▮▮▮▮▮     ] 2 just
[▮▮▮▮▮     ] 2 atuin
[▮▮        ] 1 wc
[▮▮        ] 1 code
Total commands:   12
Unique commands:  14

Notice that I was able to set the --filter-mode to look only at my session history.

You can also pass time arguments like this

atuin stats last thursday 3pm

This will get you everything since last Thursday at 3pm

  1. atuin store status

The atuin store status command can tell you the status of each machine that has atuin attached to it. The host it displays is a uuid. You can get a pretty hostname, but you need to run

printf '%s %s\n' (cat ~/.local/share/atuin/host_id) (hostname -s)

on each machine.

  1. atuin scripts The atuin scripts command is currently an undocumented feature. There was a blog post announcing it though.

I continue to struggle with how this is helpful, BUT this doesn't have to be useful for me to be useful in general!

One thing that does look interesting is this invocation:

atuin scripts new script-name --last N --tags my-tag --description "Does the thing!"

This will take the last N commands, add a tag called my-tag and a description Does the thing! and generate a script you can run with atuin scripts run script-name

If you run atuin scripts list you'll get this output

Available scripts:
- script-name [tags: my-tag]
  Description: Does the thing!

This can be especially helpful if you are going to need to do the same thing over and over again (like maybe migrating to a self hosted version of Atuin for all of your servers).

  1. Key Bindings

This is where we can control, amongst other things, whether or not the up-arrow will invoke Atuin or not. One thing I've been struggling with is that in Herdr the up arrow is captured by a scrolling up mouse as well. This is frustrating for me when I want to scroll up the page output in a shell. I instead have to remember to use the Page-up key instead. I am not a fan.

So, I updated my key bindings to eliminate the up-arrow behavior for Atuin so that the mouse scroll doesn't get stolen by Atuin.

See the docs for full details.

Settings

There are a LOT of settings. Like, holy shit, so many settings!

I'll give a run down of some of the ones that I found most interesting

  • sync_address: This is what allows you to have your own self hosted instance. It defaults to "https://api.atuin.sh" so if you don't run your own instance there's nothing you need to update
  • filter_mode: this is what allows you to determine which history you start your search in. The default is global, but I changed it to host. You can use ctrl-r to cycle through the filter_mode though should you need to
  • inline_height: the default is 40, but I set mine to 15 because I liked the way it looked better. This setting will determine the maximum number of lines Atuin's interface should take up.
  • enter_accept: This defaults to false but it kept breaking my brain, so I updated it to true. When this is set to true, Atuin will immediately execute a command rather than the user having to press enter twice. Pressing tab returns me to the shell and allows me a chance to edit the command if I want.
  • theme: allows you to set your theme to a custom theme. I've set mine to dracula

Rabbit Hole

While working through this I decided that setting up my own atuin sync server would be fun (and maybe easy). Since I use coolify for my various web projects, I decided to use it to stand up my own sync server using the following docker-compose.yml file (created with the help of Claude):

services:
  atuin:
    restart: always
    image: ghcr.io/atuinsh/atuin:62d5bad
    command: start
    expose:
      - 8888
    environment:
      ATUIN_HOST: "0.0.0.0"
      ATUIN_OPEN_REGISTRATION: "true"
      ATUIN_DB_URI: postgres://${ATUIN_DB_USERNAME}:${ATUIN_DB_PASSWORD}@db/${ATUIN_DB_NAME}
      RUST_LOG: info,atuin_server=debug
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:18
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${ATUIN_DB_USERNAME} -d ${ATUIN_DB_NAME}"]
      interval: 5s
      timeout: 5s
      retries: 10
    volumes: # Don't remove permanent storage for index database files!
      - "atuin-db:/var/lib/postgresql"
    environment:
      POSTGRES_USER: ${ATUIN_DB_USERNAME}
      POSTGRES_PASSWORD: ${ATUIN_DB_PASSWORD}
      POSTGRES_DB: ${ATUIN_DB_NAME}
      TZ: America/Los_Angeles
      PGTZ: America/Los_Angeles

volumes:
  atuin-db:

There is one limitation with rolling your own sync server. If you want to use the AI features of Atuin you need to roll your own AI server too. They do have a setup guide for that, but it seemed like a solution to a problem I don't have, so I decided against it.