Backups and restore

What to back up, how often, and how to put it back.

Three things hold state: the PostgreSQL database, the attachment bucket, and the two secrets in .env. Back up all three; the database without the secrets cannot read stored credentials, and the database without the bucket has attachment records with no files.

Database

With the compose setup:

docker compose exec -T db pg_dump -U alba -Fc alba > alba-$(date +%F).dump

Restore into an empty database, then start the application, which runs any newer migrations:

docker compose up -d db
docker compose exec -T db pg_restore -U alba -d alba --clean --if-exists < alba-2026-09-10.dump
docker compose up -d

Daily dumps kept for a month are a reasonable default. PostgreSQL continuous archiving works too; nothing in the schema needs special handling.

Attachments

Attachment files are objects in the bucket, named by ticket and attachment id. Back the bucket up with your storage provider's tooling: versioning and replication on S3, or a scheduled rclone sync or aws s3 sync from the bundled RustFS to another bucket. Restoring is copying the objects back under the same keys; the application does not need to know.

Secrets

Store .env (or at least SECRET_KEY_BASE and CLOAK_KEY) in your secrets manager. Restoring on a new host with a different CLOAK_KEY leaves single sign-on secrets, storage credentials and webhook secrets unreadable; re-enter them in Administration in that case.

Search index

The Typesense index needs no backup: it is derived from the database and rebuilt with Rebuild index on the Search page under Administration. After restoring a database or starting a fresh Typesense instance, run a rebuild. A rebuild fills a new index and switches to it when complete, so an existing index keeps answering meanwhile; a fresh instance matches only exact ticket keys until the rebuild completes, while everything else works.

Workspace exports

An administrator can export the whole workspace, attachments included, to one .tar.gz file under Administration, Export and import, and import such a file into another installation or a hosted workspace (see the user guide). An export is a way to move, not a backup strategy: it leaves out passwords and the secrets of integrations and single sign-on, and it is made when someone asks for it. Keep backing up the database and the bucket.

The export and import jobs unpack the archive in the container's temporary directory and remove it afterwards, so that directory needs free space of about twice the archive's size while one runs. An archive is uploaded to storage in one piece, which S3-compatible stores limit to 5 GB. For a larger workspace, export and import on the server, where there is no limit:

docker compose exec app bin/alba eval 'Alba.Release.export_workspace("default", "/tmp/workspace.tar.gz")'
docker compose cp app:/tmp/workspace.tar.gz .
docker compose cp workspace.tar.gz app:/tmp/workspace.tar.gz
docker compose exec app bin/alba eval 'Alba.Release.import_workspace("default", "/tmp/workspace.tar.gz")'

"default" is the workspace of a self-hosted installation, and /tmp is the container's own temporary directory, which is why the archive is copied in and out. An import from the command line does not keep any existing account logged in, so if the export holds no administrator you can use, grant one afterwards with Alba.Release.grant_admin/2. The importing installation must be at least as new as the one that made the export.

Jira backups

A backup uploaded for an import waits in the workspace's storage for a day and is then removed; keep your own copy if you may want to import it again on a rebuilt installation.