Atuin on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and use of Atuin on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Atuin is a self hosted sync server for magical shell history. The Atuin terminal client records your shell history into an encrypted, searchable database and, with this server, synchronises it across all of your machines. Because history is encrypted end to end with a key that never leaves your own machines, the server only ever stores opaque encrypted records, so your command history stays private on infrastructure you control instead of a third party service.
The image installs the official prebuilt Atuin server Rust binary, pinned to a release version and checksum verified, and runs it 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.
Secure by design — open registration disabled. Atuin's server normally allows anyone to register an account, which turns a public deployment into an open sign up service. This image sets open_registration = false in /etc/atuin/server.toml, so the appliance is single tenant. The guide below shows how to enable registration if you want to invite more people.
Secure by design — no baked credential. Nothing is baked into the image. On first boot the PostgreSQL atuin role password and a per machine Atuin account are generated with independent random values, the account is seeded so the sync API is provably live, and 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. The client side end to end encryption key is always generated on your own machine by the Atuin client and is never stored on the server.
What is included:
-
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 by default, so the server is single tenant rather than an open sign up service
-
A per machine Atuin account and PostgreSQL password generated on first boot, summarised in a root only file, never baked into the image
-
A demo history record seeded on first boot so the sync API is provably live
-
Unattended security upgrades left enabled so the appliance keeps receiving patches
Prerequisites
-
Active Azure subscription, SSH public key, VNet + subnet in the target region
-
Subscription to the Atuin listing on Azure Marketplace
-
A Network Security Group rule allowing TCP 22 (admin) and TCP 80 (the sync API) from the networks that need them
Recommended virtual machine 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.
Step 1: Deploy from the Azure Portal
Search Atuin in Marketplace, select the cloudimg 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 — the bundled PostgreSQL is loopback only.
Step 2: Deploy from the Azure CLI
RG="atuin-prod"; LOCATION="eastus"; VM_NAME="atuin1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/atuin-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--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 1002
Step 3: First boot
On first boot the image starts PostgreSQL on the loopback interface, generates a per VM password for the atuin database role, recreates a clean application database, seeds a per machine Atuin account plus 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.txt
The file contains the sync address, the seeded account username, its password and the PostgreSQL password. Keep it safe.
Step 4: Confirm the service is healthy
The Atuin server, nginx and PostgreSQL are all active, the 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/healthz

Step 5: Open registration is disabled by default
The appliance is single tenant: open_registration is false, so a fresh registration attempt against the sync API is refused with HTTP 400. This is what stops 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/register

Step 6: The sync API is live — account round-trip
The per machine account seeded on first boot authenticates through the real sync API. Here we log in for a session token, confirm the account with the authenticated /api/v0/me endpoint, store a history record, and read the record count back — 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 .count

Step 7: The PostgreSQL backing store
Atuin stores its accounts, sessions and encrypted history and record data in the bundled PostgreSQL 16 database. The schema is created automatically the first time the server starts.
sudo -u postgres psql -d atuin -c '\dt'

Step 8: 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. These commands run on your own machine, not on the VM:
# On your own machine (see https://docs.atuin.sh):
atuin login --username cloudimg --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 server
Set the sync address permanently in your client config (~/.config/atuin/config.toml):
sync_address = "http://<your-vm-address>/"
Step 9: 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 back off 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.service
Step 10: Add TLS
The nginx reverse proxy is ready for a certificate. Point a DNS name at the VM, open TCP 443 on the NSG, and 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.
Step 11: Security recommendations
-
Keep the database private. The bundled PostgreSQL is 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 TCP 80 or 443 only from the networks that sync.
-
Leave open registration off. Enable it only while inviting 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
atuindatabase. 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.
Step 12: Support and Licensing
Atuin is developed by the Atuin project and distributed under the MIT license. This cloudimg image bundles the unmodified official Atuin server binary. cloudimg provides the packaging, the secure by default loopback only configuration with open registration disabled, the PostgreSQL backing store and nginx reverse proxy, the first boot credential generation, and 24/7 support with a guaranteed 24 hour response SLA.
cloudimg 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 cloudimg. Deploy from the Portal or the Azure CLI as shown above.
Need Help?
Email support@cloudimg.co.uk for deployment help, configuration questions, or licensing enquiries.