Atuin
Overview
Atuin is a self-hosted sync server for shell history. The Atuin terminal client records your shell history into an encrypted, searchable database, and this server synchronizes it across all of your machines. History is encrypted end to end with a key that never leaves your own machines, so the server stores only opaque encrypted records and your command history stays private on infrastructure you control.
This SecureAnyCloud image runs the official prebuilt Atuin server Rust binary on Ubuntu 24.04 on Azure. The binary is pinned to a release version, checksum verified, and run under systemd as a dedicated service bound to the loopback interface. A local PostgreSQL 16 instance is the backing store, and nginx reverse proxies the sync API on port 80, ready for your TLS certificate.
What you get:
- The official Atuin server binary (
atuin-server), checksum verified and run under systemd, bound to the loopback interface. - A local PostgreSQL 16 backing store, listening on the loopback interface only.
- An nginx reverse proxy on port 80, ready for your TLS certificate.
- Open registration disabled. Atuin's server normally lets anyone register an account, which turns a public deployment into an open sign-up service. This image sets
open_registration = falsein/etc/atuin/server.toml, so the appliance is single-tenant. Step 9 shows how to enable registration to invite more people. - No baked credentials. On first boot, the PostgreSQL
atuinrole password and a per-VM Atuin account are generated with independent random values. A root-only summary is written to/root/atuin-credentials.txt. The application database is dropped when the image is built, so every deployed VM starts from a clean database with fresh credentials. - A demo history record seeded on first boot, so the sync API is provably live.
- Client-side end-to-end encryption. The Atuin client generates the key on your own machine, and it is never stored on the server.
- Unattended security upgrades left enabled so the appliance keeps receiving patches.
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region.
- A subscription to the Atuin listing on Azure Marketplace.
- A Network Security Group (NSG) rule that allows TCP 22 (administration) and TCP 80 (the sync API) from the networks that need them.
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for personal or small-team sync. For a larger team or a bigger history store, use Standard_D2s_v5 or larger and attach a data disk for the PostgreSQL data directory.
Deploy from the Azure Portal
Search the Azure Marketplace for Atuin, select the SecureAnyCloud publisher, and click Create.
Configure the Network Security Group to allow TCP 22 for administration and TCP 80 for the sync API from the networks that need them. No inbound database port is required, because the bundled PostgreSQL listens on loopback only.
Deploy from the Azure CLI
Accept the Marketplace terms once per subscription, then create the VM and open ports 22 and 80:
RG="atuin-prod"; LOCATION="eastus"; VM_NAME="atuin1" IMAGE_URN="sac1647283583153:atuin-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:atuin-ubuntu-24-04:default:latest
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$IMAGE_URN" \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
# Open the admin and sync ports on the VM's NSG:
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80 --priority 1002First boot
On first boot, the image starts PostgreSQL on the loopback interface and generates a per-VM password for the atuin database role. It recreates a clean application database, seeds a per-VM Atuin account and a demo history record, and writes /root/atuin-credentials.txt. This completes within a minute.
SSH in as azureuser and read the details:
sudo cat /root/atuin-credentials.txtThe file contains the sync address, the seeded account username, its password and the PostgreSQL password. Store it somewhere safe.
Confirm the service is healthy
Confirm that the Atuin server, nginx and PostgreSQL are all active. The Atuin server binds only to the loopback interface, and nginx serves the unauthenticated /healthz endpoint on port 80.
systemctl is-active atuin.service nginx.service postgresql.service echo '--- atuin and PostgreSQL are loopback only (no public app/db port) ---' ss -tlnH | grep -E ':8888 |:5432 ' | awk '{print $4}' | sort -u echo -n '/healthz through nginx :80 -> HTTP ' curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthzConfirm open registration is disabled
The appliance is single-tenant. Because open_registration is false, a new registration attempt against the sync API is refused with HTTP 400. This keeps a public deployment from becoming an open sign-up service.
echo '--- server.toml: loopback bind + open registration disabled ---' sudo grep -E '^(host|port|open_registration)' /etc/atuin/server.toml
echo -n 'POST /register (should be refused) -> HTTP ' curl -s -o /dev/null -w '%{http_code}\n' -H 'Content-Type: application/json' \
-d '{"username":"someone","email":"someone@example.com","password":"changeme12345"}' \
http://127.0.0.1/registerTest the sync API with an account round-trip
The per-VM account seeded on first boot authenticates through the real sync API. The commands below log in for a session token and confirm the account at the authenticated /api/v0/me endpoint. They then store a history record and read the record count back, which is the same round-trip the Atuin client performs when it syncs.
U=$(sudo grep '^ATUIN_ACCOUNT_USERNAME=' /root/atuin-credentials.txt | cut -d= -f2-)
P=$(sudo grep '^ATUIN_ACCOUNT_PASSWORD=' /root/atuin-credentials.txt | cut -d= -f2-)
TOKEN=$(curl -s -H 'Content-Type: application/json' -d "{\"username\":\"$U\",\"password\":\"$P\"}" http://127.0.0.1/login | jq -r .session)
echo "Logged in as $U; session token: ${TOKEN:0:12}..."
echo -n 'GET /api/v0/me -> '; curl -s -H "Authorization: Token $TOKEN" http://127.0.0.1/api/v0/me echo;
echo -n 'History records before: '; curl -s -H "Authorization: Token $TOKEN" http://127.0.0.1/sync/count | jq -r .count curl -s -o /dev/null -H "Authorization: Token $TOKEN" -H 'Content-Type: application/json' \
-d "[{\"id\":\"guide-$(date +%s%N)\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"data\":\"encrypted-history-blob\",\"hostname\":\"my-laptop\"}]" \
http://127.0.0.1/history
echo -n 'History records after: '; curl -s -H "Authorization: Token $TOKEN" http://127.0.0.1/sync/count | jq -r .countThe PostgreSQL backing store
Atuin stores its accounts, sessions, and encrypted history and record data in the bundled PostgreSQL 16 database. The server creates the schema automatically the first time it starts. List the tables:
sudo -u postgres psql -d atuin -c '\dt'Connect the Atuin client
On each of your own machines, install the Atuin client, point it at this server, and log in with the seeded account (or your own, once you enable registration). Generate your encryption key on your first machine and copy it to the others. The key never touches the server.
Run these commands on your own machine, not on the VM:
# On your own machine (see https://docs.atuin.sh): atuin login --username sac --password '<password from /root/atuin-credentials.txt>' \
--sync-address http://<your-vm-address>/ atuin key
# print your encryption key (run on the first machine) atuin sync
# sync your shell history through your serverTo set the sync address permanently, add it to your client config (~/.config/atuin/config.toml):
sync_address = "http://<your-vm-address>/"Invite more people (enable registration)
To let other people register their own accounts, set open_registration = true in /etc/atuin/server.toml on the VM and restart the service. Turn it off again once everyone has registered.
sudo sed -i 's/^open_registration = false/open_registration = true/' /etc/atuin/server.toml
sudo systemctl restart atuin.service
# ... let people run: atuin register -u <name> -e <email> -p <password> --sync-address http://<your-vm-address>/
# then disable it again:
sudo sed -i 's/^open_registration = true/open_registration = false/' /etc/atuin/server.toml
sudo systemctl restart atuin.serviceAdd TLS
The nginx reverse proxy is ready for a certificate. Point a DNS name at the VM and open TCP 443 on the NSG. Install a certificate with your preferred tool, for example certbot with the nginx plugin.
Then set the sync address on your clients to the https:// URL.
Security recommendations
Keep the database private. The bundled PostgreSQL listens on loopback only, and no database port is exposed. Never expose port 5432 or 8888 publicly. Reach the server only through nginx.
Restrict the NSG. Allow TCP 22 from your management network, and allow TCP 80 or 443 only from the networks that sync.
Leave open registration off. Enable it only while you invite users, then disable it again (Step 9). Anyone who can register can store encrypted blobs on your server.
Use TLS in production. Serve the sync API over HTTPS (Step 10) so client tokens are never sent in clear text.
Back up PostgreSQL. Your accounts and encrypted history live in the atuin database. Back it up like any production database, and consider an encrypted data disk for the data directory.
Keep the OS and Atuin patched. Unattended security upgrades remain enabled on the running VM. Upgrade the Atuin server binary when new releases ship.
Support and Licensing
Atuin is developed by the Atuin project and distributed under the MIT license. This SecureAnyCloud image bundles the unmodified official Atuin server binary.
SecureAnyCloud provides the packaging, the secure-by-default configuration (loopback only, open registration disabled), the PostgreSQL backing store and nginx reverse proxy, first-boot credential generation, and 24/7 support with a guaranteed 24 hour response SLA.
SecureAnyCloud is not affiliated with or endorsed by the Atuin project. Atuin is a mark of its respective owner and is used here only to identify the software.
Deploy on Azure
Find Atuin on Ubuntu 24.04 LTS on the Azure Marketplace, published by SecureAnyCloud. Deploy from the Portal or the Azure CLI as shown above.
Need Help?
Email sales@secureanycloud.com for deployment help, configuration questions, or licensing enquiries.
Deploy on Azure
Launch Atuin on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
Atuin is a self-hosted sync server for shell history. The Atuin terminal client records your shell history into an encrypted, searchable database, and this server synchronizes it across all of your machines. History is encrypted end to end with a key that never leaves your own machines, so the server stores only opaque encrypted records and your command history stays private on infrastructure you control.
This SecureAnyCloud AMI is a ready-to-use appliance. The services postgresql.service, atuin.service and nginx.service are enabled and start on boot, so the sync backend is already answering when you connect over SSH. Atuin has no web interface: its client is a terminal tool that you run on your own machines.
What you get:
- The Atuin server binary bound to the loopback interface (
127.0.0.1:8888), fronted by an nginx reverse proxy on port 80, ready for your TLS certificate. - Open registration disabled. Atuin's server normally allows open registration, which would turn a public deployment into an open sign-up service. This image disables it, so the server is single-tenant.
- No default password and no baked secret. A unique PostgreSQL password and a per-instance account are generated on the first boot of every instance and written to a root-only credentials file.
- A shipped security group that opens port 22 only. The sync endpoint is not exposed to the network until you deliberately open port 80 or 443 behind your own access controls.
- A dedicated, independently resizable EBS data volume mounted at
/var/lib/postgresqlfor the PostgreSQL data directory. Your encrypted history database grows on durable storage that you can expand without touching the boot volume.
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances.
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products.
- An EC2 key pair in the target Region for SSH access to the instance.
- A VPC and subnet in the target Region, with a security group that allows inbound port 22 from your management network.
- The Atuin terminal client installed on the machines whose history you want to sync (see the upstream Atuin documentation).
- The AWS CLI (version 2) installed locally, if you plan to deploy from the command line.
Launch the instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for Atuin. Select the SecureAnyCloud listing, choose Select, then choose Continue on the subscription summary.
Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings, select your VPC and subnet, then create or select a security group that opens port 22 from your management network. Leave the root volume at the default size or larger.
Select Launch instance. First-boot initialization takes a short time after the instance state becomes Running and the status checks pass. The image generates the per-instance database password, rotates the PostgreSQL role, seeds the per-instance account, and starts the Atuin server against it.
Launch the instance from the AWS CLI
The following command launches an instance from the SecureAnyCloud Atuin Marketplace AMI into an existing subnet and security group.
Replace <ami-id> with the AMI ID shown on the Marketplace listing and <key-name> with your EC2 key pair name. Replace <subnet-id> with your subnet ID and <security-group-id> with a security group that opens port 22 from your management network.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--metadata-options "HttpTokens=required" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=atuin}]'When the instance state is Running and both status checks pass, note its public or private IP address for the next step.
Connect over SSH
Connect to the instance with your key pair. On the Ubuntu 24.04 variant, the SSH login user is ubuntu.
Connecting to your instance
ssh -i /path/to/<key-name>.pem ubuntu@<public-ip>Confirm the services are running
The appliance runs three services: postgresql.service (the backing store), atuin.service (the sync server, bound to loopback) and nginx.service (the reverse proxy on port 80). Confirm that all three are active and check the Atuin server version.
systemctl is-active atuin nginx postgresql atuin-server --versionactive active active atuin-server 18.17.1The Atuin server binds only to the loopback interface on port 8888. nginx is the single public listener on port 80, and PostgreSQL listens only on loopback.
sudo ss -tlnp | grep -E '127.0.0.1:8888|:80 |127.0.0.1:5432'Check the sync API health
nginx serves the sync API on port 80 and proxies to the loopback Atuin server. When the server is healthy, the unauthenticated health endpoint returns HTTP 200 with a JSON status:
curl -s http://127.0.0.1/healthz{"status":"healthy"}Read the per-instance credentials
On first boot, the image generates a unique per-instance account and PostgreSQL password and writes them to a root-only file. Read them with:
sudo cat /root/atuin-credentials.txtThe file records the sync address, the account username (SecureAnyCloud), the account password and the PostgreSQL password. Point your Atuin client at the ATUIN_SYNC_ADDRESS shown in the file. Treat the file and its values as secrets.
Connect the Atuin client and sync
On each machine whose history you want to sync, install the Atuin terminal client, then set the sync address and log in. Atuin generates its end-to-end encryption key on your own machine, and the key never leaves it. The server stores only opaque encrypted blobs.
On your first machine, run atuin key to display the encryption key and store it safely. Supply the same key when you log in on your other machines.
# On the client machine (not the server):
export ATUIN_SYNC_ADDRESS="http://<public-ip>/" atuin login -u sac -p '<account-password>' -k '<your-encryption-key>' atuin syncYour shell history now syncs across every machine you log in from, with fast full-text and contextual search available through the Atuin client. After the first sync, atuin sync runs automatically in the background.
Add more users (optional)
The server ships single-tenant with open registration disabled, which is why an unauthenticated registration attempt is refused with HTTP 400.
To let additional people register their own accounts, set open_registration = true in /etc/atuin/server.toml and restart the server. Enable this only behind your own network controls, because it lets anyone who can reach the sync endpoint create an account.
sudo sed -i 's/^open_registration = false/open_registration = true/' /etc/atuin/server.toml
sudo systemctl restart atuin.serviceExpose the sync endpoint safely
nginx fronts the Atuin server on port 80, but the shipped security group opens port 22 only. The sync endpoint is not reachable from the network until you open it.
To let your machines reach the server, open port 80 (or 443 once you have added TLS) in the instance security group to your trusted network only. Put a TLS certificate in front of it before you sync over the public internet. History is already end-to-end encrypted, so the server never sees your plaintext, and TLS protects the transport and your session token.
# Example: allow HTTPS from your office network only aws ec2 authorize-security-group-ingress \
--group-id <security-group-id> \
--protocol tcp --port 443 --cidr <your-mgmt-cidr>Locate the history database and data volume
Atuin stores its encrypted history records in the local PostgreSQL database atuin. Its data directory lives on the dedicated EBS data volume mounted at /var/lib/postgresql.
Back up the database with the standard PostgreSQL tools. Grow the volume with the standard EBS resize workflow, without touching the boot volume. Confirm the mount:
findmnt /var/lib/postgresql df -h /var/lib/postgresqlTARGET SOURCE FSTYPE OPTIONS /var/lib/postgresql /dev/nvme1n1 ext4 rw,relatime,nofailSupport
SecureAnyCloud supports this image 24/7. Contact SecureAnyCloud support for help with Atuin server deployment, connecting the Atuin terminal client and configuring sync, enabling or disabling registration, TLS and reverse-proxy setup, version upgrades, and PostgreSQL backup and administration.
Atuin is distributed under the MIT license. All product and company names are trademarks or registered trademarks of their respective holders. Their use does not imply any affiliation with or endorsement by them.
Deploy on AWS
Launch Atuin on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com