Coming soon. Depotex is in active development, so features and these docs may still change.

Backups

Back up the database and platform files, check and test-restore every backup, encrypt them, keep them in a separate place, and restore one, even onto a new server

1. Overview

Settings → Backups takes a backup of this installation's database and its platform files: avatars, branding, plugin packages and uploaded SBOM documents. Every backup is read back and checked as soon as it's written, and its SHA-256 checksum is published next to it so you can check a downloaded copy. A verified backup can be restored over the installation, or onto a new server in its setup wizard. Available on every license tier, to super admins; test restores need Enterprise.

The page has three tabs: Backups (the list, under a summary of the backup target, encryption and the latest test restore), Restores (every restore and how far it got) and Settings (backup target, encryption and test restores). Change in the summary opens Settings.

Settings → Backups: the summary of target, encryption and latest test restore over the list of backups with status, size, Keep, expiry and actions
Settings → Backups: the summary of target, encryption and latest test restore over the list of backups with status, size, Keep, expiry and actions

Uploaded artifacts are not in a backup. Packages, images and files published to repositories stay in your storage and must be backed up with your storage platform's own tools: see Backing up artifact storage.

This backup is a convenience for small and medium installations. For a very large or very busy installation, rely on your database platform's own backups (point-in-time recovery, snapshots): a backup holds one consistent snapshot of the database for as long as the dump runs.

2. Take a backup

  1. Open — Go to Settings → Backups and choose Take backup.
  2. Options — Tick Keep permanently to stop the cleanup job from ever deleting it, and add an optional Note (for example "before upgrading to 1.4").
  3. Wait — The backup runs in the background. Its row shows Running, then the result of its check (Checking, then Verified (level 3)). You can leave the page.

Only one backup runs at a time across all servers; starting another while one runs says so. A backup is cancelled if it runs longer than BACKUP_MAX_DURATION (6 hours).

3. The list of backups

ColumnWhat it shows
StartedWhen it started, whether it was manual or scheduled, and who took it
StatusRunning or Failed (with the reason) while it's taken; then its check: Check pending, Checking, Verified (level 3) or Check failed. Marks for Encrypted, In another backup target and the latest test restore, and a warning when the signature doesn't match or comes from another installation
SizeThe size of the backup file
KeepKeep permanently: switch it on or off at any time
ExpiresWhen the cleanup job will delete it, or Never
ActionsRestore on a verified backup, Download, and the ⋯ menu: Check again, Test restore (Enterprise) and Delete

The arrow at the start of a row opens its details: the SHA-256 checksum with a button to copy it, the kind and who took it, the note, where an uploaded backup came from, and the newest test restore. What a backup holds, under the list, folds out the notes on contents and the backup jobs.

4. How a backup is checked

Right after a backup is written, Depotex reads the whole file back from storage and checks it in three levels. Check again repeats the check at any time, for example after copying backups between storage locations.

  1. Level 1 — checksum: the SHA-256 of the whole file matches the one recorded when it was written (and published in its .sha256 file).
  2. Level 2 — structure and signature: the archive opens, its manifest is signed by this installation's backup key, and every file inside matches its own checksum.
  3. Level 3 — database dump: pg_restore --list reads the database dump's table of contents.
  4. Level 4 — test restore (Enterprise): the dump is loaded into a temporary database and every table's row count compared with the backup's. See Test restores.

A backup encrypted to public keys is checked up to level 2: the server doesn't hold the private key, so its dump is opened only when it's restored or test-restored with the key.

A backup that fails its check is marked Check failed with the reason, and super admins get a critical alert.

5. Download a backup and check it

Download saves the backup as a .dxbak file (a tar archive). Downloads are recorded in the audit log and need the backups:download permission, because a backup holds the whole database. Next to every backup, Depotex keeps a .sha256 file in sha256sum format; check a download against it:

curl -OJ -H "Authorization: Bearer $DEPOTEX_TOKEN" https://depotex.example.com/api/backups/$ID/download
curl -H "Authorization: Bearer $DEPOTEX_TOKEN" https://depotex.example.com/api/backups/$ID/checksum | sha256sum -c

Keep downloaded backups somewhere other than the installation's own storage, or set a separate backup target.

The archive holds a signed manifest.json (versions, checksums), files.json, the database dump db.dump (PostgreSQL custom format) and the platform files under files/; in an encrypted backup the dump and files are inside one encrypted payload.age instead. Settings and plugin secrets in the dump are encrypted with the installation's CONFIG_ENCRYPTION_KEY: keep that key with your backups, since a restore needs the same one.

6. Restore a backup

A restore replaces this installation's database and platform files with a backup's. Everyone is signed out, and every server shows a maintenance screen until it ends. Needs the backups:restore permission (super admins).

The Restore dialog: the last pre-checks, what happens, and the installation name and password that confirm it
The Restore dialog: the last pre-checks, what happens, and the installation name and password that confirm it
  1. Choose the backup — On Settings → Backups, choose Restore on a verified backup (level 3, or level 2 for one encrypted to public keys).
  2. Read the checks — The dialog runs every pre-check (below) and says what will be replaced. Nothing changes yet. Restore stays off while a check fails.
  3. Give the key — For an encrypted backup, paste the private key or load it from a file, or type the passphrase (optional when Depotex still holds it). The key is tried on the backup before anything stops.
  4. Confirm — For a backup from another installation, tick that you want it anyway. Type the installation's name and your password, then Restore.
  5. Wait — Every server shows the maintenance screen with the current step. When it ends, sign in again with an account from the backup.
The maintenance screen every server shows while a restore runs, with the current step
The maintenance screen every server shows while a restore runs, with the current step
Pre-checkFails when
BackupIt isn't completed and verified
StorageIts file can't be read
SignatureThe signature isn't valid. Signed by another installation: a warning you must accept
KeyIt was made with a different CONFIG_ENCRYPTION_KEY
VersionIt comes from a newer Depotex version, or has data this version doesn't know
PostgreSQLpg_restore is missing or older than the dump (an older database server only warns)
IdleA backup or another restore is running
ExtensionsA PostgreSQL extension is installed inside a schema the restore replaces
Roles and swapThe database user can't create plugin roles (needs CREATEROLE) or rename the schemas

What the restore does, in order: every server stops taking requests (they answer 503 restore_in_progress) and pauses its jobs; a safety backup of the current database is taken; the backup is checked again and staged; the current database is set aside; the backup is loaded and brought up to this version; the platform files are written back (files added since the backup stay); signed-in sessions, OAuth tokens and plugin caches are reset, and people erased since the backup (GDPR) are erased again; then every server restarts on the restored database. API tokens from the backup keep working.

If anything fails, the previous database comes back on its own, and the servers restart on it. The same happens if the server running the restore stops: another server, or the next one to start, rolls it back.

The Restores tab lists every restore (a running one with a bar of its steps) with its outcome. After a successful restore the previous database is kept for 24 hours (BACKUP_RESTORE_KEEP_PRIOR) in case you need it; Discard previous database frees the space sooner.

The database briefly holds two copies (the previous and the restored one), and the server running the restore needs room in BACKUP_TEMP_DIR for the staged backup. Depotex doesn't check free space first.

7. Encryption

Under Encryption on the Settings tab of Settings → Backups, switch on Encrypt new backups (off by default) to encrypt the database dump and platform files of every new backup. The manifest stays readable, so the list still shows what each backup is. Existing backups aren't changed.

The Encryption section: Encrypt new backups, public keys or passphrase, and Generate a key pair
The Encryption section: Encrypt new backups, public keys or passphrase, and Generate a key pair
ModeHow it worksRestore needs
Public keys (recommended)Backups are encrypted to up to 20 age public keys (age1…). Depotex never holds a private key, so someone who takes the server can't open the backups.One of the private keys
PassphraseA passphrase of at least 12 characters, stored encrypted with CONFIG_ENCRYPTION_KEY, so backups are checked fully and test-restored on schedule.Nothing while Depotex holds the passphrase; the passphrase on a new server

Generate a key pair makes a new age key pair in the browser's session: copy or download the private key and keep it safe, then tick that you stored it to add the public key. The private key is shown once and stored nowhere. You can also add a public key made with the age-keygen tool.

Without the private key (or the passphrase), an encrypted backup can't be restored by anyone, including Depotex support.

8. Add a backup from elsewhere

To restore a backup that isn't in the list (downloaded earlier, or from another installation), choose Add a backup at the top of Settings → Backups. Needs backups:restore.

The Add a backup dialog, Upload tab: choose a .dxbak file and, optionally, its expected SHA-256
The Add a backup dialog, Upload tab: choose a .dxbak file and, optionally, its expected SHA-256
  • Upload: choose the .dxbak file and, optionally, paste its SHA-256 (or the line of its .sha256 file). The file goes up in 16 MiB pieces (BACKUP_UPLOAD_CHUNK_SIZE), each checked on arrival; pause and resume at any time, and if the connection drops, choosing the same file again carries on where it stopped. An unfinished upload is dropped after 24 hours (BACKUP_UPLOAD_EXPIRY).
  • Import folder: for a file too large for the browser, an operator copies it into backups/import/ in the backup storage (with its .sha256 file if there is one). The Import folder tab lists the files there with what each one is; Check adds one to the list.
The Add a backup dialog, Import folder tab: files with their version, date and state, and Check
The Add a backup dialog, Import folder tab: files with their version, date and state, and Check

Either way the backup is checked like any other, then offered for Restore. A backup from another installation shows Signed by another installation; restoring it needs your consent and the same CONFIG_ENCRYPTION_KEY. Uploaded and imported backups are never deleted by the cleanup job.

9. Disaster recovery

When the installation itself is lost, restore a backup onto a new server in one of two ways. Both need the backup's CONFIG_ENCRYPTION_KEY.

10. In the setup wizard

Start a new server as for a fresh installation and go through the setup wizard with the same license, database server and storage as before (a new, empty database). On the last step choose Restore from a backup instead of creating an administrator:

The setup wizard's last step with Restore from a backup: the chosen backup's summary, key field and consent box
The setup wizard's last step with Restore from a backup: the chosen backup's summary, key field and consent box
  1. Choose the backup — Upload the .dxbak file (sent in pieces; a broken connection resumes), or pick one already in backups/import/ of the storage you entered.
  2. Check it — The wizard shows when and by which version it was made, and whether it's encrypted. It must have been made with this server's CONFIG_ENCRYPTION_KEY.
  3. Key and consent — Give the key of an encrypted backup, tick that you want to restore it, and choose Complete.
  4. Wait and sign in — The server restores the backup on its first start and then shows the sign-in page. Sign in with an administrator from the backup.

The wizard's license, storage, Redis and installation ID are kept; everything else (people, settings, data) is the backup's. If the restore fails, the server opens the wizard again with the reason; retry, or create an administrator instead. An uploaded backup waits on the setup server's disk (SETUP_UPLOAD_DIR, default ./data/setup-upload) until setup completes, so that disk needs room for it. A backup kept on a separate backup target must be uploaded or copied into the import folder of the installation's storage.

11. From the command line

On an installation that is already set up, restore a file without the web app. Stop every other server first: the command refuses while others are running, unless --restore-force.

# check only: prints every pre-check, changes nothing
CONFIG_ENCRYPTION_KEY=... ./server --restore-file /backups/depotex-2026-10-11.dxbak
# restore
CONFIG_ENCRYPTION_KEY=... ./server --restore-file /backups/depotex-2026-10-11.dxbak --yes
OptionMeaning
--restore-file <file>Copies the file (and its .sha256) into the import folder and restores it
--restore-import <name>Restores a file already in backups/import/
--yesRestore; without it only the checks run
--restore-key-file <file>The key of an encrypted backup: an age identity file, or a file holding the passphrase
--restore-sha256 <hash>The SHA-256 the file must have
--restore-accept-foreignRestore a backup signed by another installation
--restore-forceRestore although other servers report as running

The command exits when it's done; start the servers normally afterwards. The first one to start finishes the restore (sessions reset, erasures replayed).

12. Backup target

By default backups are kept in the installation's own storage, under backups/, and Settings → Backups and the system check warn about it: losing the storage loses the backups too. On the Settings tab, under Backup target, choose A separate location (a local path shared by every server, Amazon S3, MinIO or Azure Blob Storage) for backups, checksum files, the import folder and uploads.

The Backup target section: a separate S3 location with Test and Save, and the warning shown while the installation's storage is used
The Backup target section: a separate S3 location with Test and Save, and the warning shown while the installation's storage is used
  • Test checks that Depotex can write, read and delete there; Save tests again and writes a marker file, backups/.depotex-target. Every server checks that marker before writing, so a local path that isn't the same shared volume on every server is refused.
  • An installation's file storage can't be a backup target. A location another installation uses asks you to take it over; one this installation used before becomes its target again, with its backups.
  • Backups stay where they were written: one in another target is marked In another backup target, and can't be downloaded, checked or restored until that target is set again. Open uploads are dropped when the target changes.
  • The platform files inside a backup still come from, and are restored to, the installation's storage.

13. Test restores

A test restore proves a backup really restores, without touching anything in use. Enterprise license; backups:manage. Choose Test restore in a verified backup's ⋯ menu (an encrypted one asks for its key): Depotex loads the dump into a temporary database on the same PostgreSQL server, compares every table's row count with the counts recorded when the backup was taken, and drops the temporary database. The row marks the newest result and opens on it, with any tables whose counts differ; the summary above the list shows the latest one.

A backup row with its test restore result: passed, tables and rows compared, duration
A backup row with its test restore result: passed, tables and rows compared, duration
  • The weekly Backup test restore job (backup-test-restore, Sundays at 04:00, BACKUP_TEST_RESTORE_SCHEDULE) tests the newest verified backup it can open. A failure alerts super admins, and the system check warns when the last one failed or none passed in 15 days.
  • One test restore runs at a time across all servers. The database user needs CREATEDB, and the database server needs room for a second copy while it runs.
  • Owners and permissions, and platform files, aren't tested; a real restore checks those in its pre-checks.
  • Count rows for test restores (on by default) counts every table while each backup is taken. On a very large installation, switch it off to save a full read of the database per backup: test restores then check only that the dump loads. Backups made before row counting existed are tested the same way.

14. Scheduled backups and cleanup

Two jobs in Settings → Advanced → Scheduled jobs look after backups:

JobDefaultWhat it does
Scheduled backup (backup-scheduled)Daily at 02:00, offTakes a backup on schedule. Switch it on and set its time in Scheduled jobs. If a run was missed while every server was down, it runs once when one starts.
Backup cleanup (backup-cleanup)Daily at 03:00, onDeletes backups older than retention_days (7), with their checksum files. It never deletes a backup kept permanently, the newest verified backup, or an uploaded or imported one. It also drops the database a restore replaced once its 24 hours are up.
Backup test restore (backup-test-restore)Sundays at 04:00, on (Enterprise)Test-restores the newest verified backup
Backup upload cleanup (backup-upload-cleanup)Every 15 minutesTakes over uploads whose server stopped, and drops expired ones

Switching a job on or off, or changing its schedule, applies on every server of a cluster within one tick of the job.

15. Backing up artifact storage

Artifacts are usually by far the largest part of an installation and change all the time, so Depotex leaves them to your storage platform, which can copy them incrementally. Back up the storage location set in Settings → Storage, and leave out the backups/ folder (it holds the Depotex backups themselves; copy those separately).

StorageHow to back it up
Local diskSnapshot the volume (LVM, ZFS, a cloud disk snapshot), or copy it with rsync -a --delete to another machine. In a cluster, back up the shared volume once.
Amazon S3Turn on bucket versioning, and use S3 Replication to another bucket or region, or AWS Backup for S3.
MinIO / S3-compatibleUse bucket replication to another MinIO, or mc mirror to another bucket on a schedule; turn on versioning where the service has it.
Azure Blob StorageTurn on blob versioning and soft delete, and use object replication to another storage account, or Azure Backup for blobs.

Take the Depotex backup and the storage backup close together in time: the database lists the artifacts the storage holds.

16. Permissions

PermissionAllows
backups:readSeeing the list of backups and their checksums
backups:manageTaking, checking again, keeping and deleting backups
backups:downloadDownloading a backup (the whole database)
backups:restoreRestoring a backup, adding one by upload or from the import folder, and discarding a previous database

No role holds them: super admins can do everything, and API tokens they create can be given these permissions for automation. Changing encryption, the backup target and row counting needs backups:manage.

17. Events, alerts and metrics

  • Events backup.create, backup.update, backup.delete, backup.download, backup.restore, backup.upload, backup.settings.update, backup.target.update and backup.test_restore (before and after: policies and plugins can refuse them), backup.verify and backup.fail (after). See Events.
  • A failed restore or test restore sends super admins a critical alert with the reason.
  • System checks Backup target (warns while backups share the installation's storage, fails when the target can't be reached or its marker is wrong) and Test restores (Enterprise).
  • A failed backup or check sends super admins a critical alert, "The scheduled backup failed its integrity check" or "couldn't be written", with the reason. The scheduler's own "job is failing" alert applies to the scheduled backup too.
  • Prometheus metrics: depotex_backup_runs_total, depotex_backup_verifications_total, depotex_backup_duration_seconds, depotex_backup_last_size_bytes, depotex_backup_last_success_timestamp_seconds and depotex_backup_cleanup_deleted_total, depotex_backup_restore_runs_total, depotex_backup_restore_duration_seconds, depotex_backup_test_restore_runs_total and depotex_backup_test_restore_last_success_timestamp_seconds. Alert on the last success timestamps to catch backups or test restores that stopped.

18. When a backup fails

ReasonWhat to do
pg_dump isn't installed / is older than the databaseThe server image ships pg_dump 18. On other installs, install the PostgreSQL client tools at least as new as the database server, or point BACKUP_PG_DUMP and BACKUP_PG_RESTORE at them.
The platform files are too largeRaise BACKUP_FILES_MAX_BYTES (10 GiB), or move large files out of platform storage.
Couldn't be written to or read from storageCheck Settings → Storage with Test connection, and that the storage has room.
Stopped before it finishedIts server restarted or stopped reporting for 5 minutes. Take a new backup.
The file doesn't match its checksum / the signature isn't validThe backup was changed or damaged in storage. Delete it and take a new one, and check what else can write to the storage.
The backup target can't be used / its marker doesn't matchTest the target under Backup target. On a local path, check every server mounts the same shared volume there.
A restore was rolled backThe Restores list shows the step and reason; the installation runs on its previous database. Fix the cause (often a pre-check, space, or the key) and restore again.
The test restore can't create its databaseGive the database user CREATEDB (ALTER ROLE … CREATEDB).
The row counts differThe backup doesn't hold what the database held when it was taken. Take a new backup and test it; contact support if it happens again.

19. Configuration

Set on each server; schedules and retention saved in Scheduled jobs win over these.

VariableDefaultPurpose
BACKUP_SCHEDULE0 2 * * *Default time of the scheduled backup (off until switched on)
BACKUP_CLEANUP_SCHEDULE0 3 * * *Default time of the cleanup
BACKUP_RETENTION_DAYS7Days a backup is kept, unless kept permanently
BACKUP_MAX_DURATION6hA backup and its check are cancelled after this
BACKUP_FILES_MAX_BYTES10 GiBMost platform files one backup copies
BACKUP_PG_DUMP, BACKUP_PG_RESTOREpg_dump, pg_restoreThe PostgreSQL client tools to use
BACKUP_TEMP_DIRthe system temp folderWhere the dump and files are staged; needs room for the compressed dump plus the platform files (an encrypted backup briefly needs twice that, a restore the backup's)
BACKUP_TEST_RESTORE_SCHEDULE0 4 * * 0Default time of the weekly test restore
BACKUP_RESTORE_KEEP_PRIOR24hHow long the database a restore replaced is kept
BACKUP_UPLOAD_CHUNK_SIZE16 MiBSize of upload pieces (1 to 64 MiB); above 32 MiB raise FRONTEND_API_BODY_LIMIT too
BACKUP_UPLOAD_EXPIRY24hAn unfinished upload is dropped this long after its last piece
SETUP_UPLOAD_DIR./data/setup-uploadWhere a backup uploaded to the setup wizard waits until setup completes

On Kubernetes, the server pod's /tmp is where backups are staged: set backend.tmpSize in the Helm chart larger than a compressed dump of your database, or set BACKUP_TEMP_DIR to a larger volume. See Docker & Kubernetes.

20. API

RoutePermissionWhat it does
GET /api/backupsbackups:readBackups, newest first, with status, check, SHA-256 and expiry
POST /api/backupsbackups:manageTake a backup (keepPermanently, note)
GET /api/backups/{id}backups:readOne backup
PATCH /api/backups/{id}backups:manageKeep permanently, note
DELETE /api/backups/{id}backups:manageDelete a backup and its checksum file
POST /api/backups/{id}/verifybackups:manageCheck it again
GET /api/backups/{id}/checksumbackups:read<sha256> <file>, as in the .sha256 file
GET /api/backups/{id}/downloadbackups:downloadDownload the archive
GET /api/backups/{id}/restore/checkbackups:restoreEvery pre-check and what would be replaced; changes nothing
POST /api/backups/{id}/restorebackups:restoreRestore (confirm: the installation's name, password, acceptForeign, key)
GET /api/backups/restoresbackups:readRestores, newest first
POST /api/backups/restores/{id}/discardbackups:restoreDrop the database a restore replaced
GET /api/restore/statusnoneWhether a restore runs and its step (for the maintenance screen)
POST / GET /api/backups/{id}/test-restorebackups:manage / backups:readStart a test restore (Enterprise; optional key) / its newest result
GET / PUT /api/backups/settingsbackups:read / backups:manageEncryption, row counting and the backup target; a field left out keeps its value
POST /api/backups/settings/keypairbackups:manageA new age key pair, stored nowhere
PUT /api/backups/settings/target, POST …/target/testbackups:manageSet (separate, storage, adopt) or test the backup target
GET / POST /api/backups/importsbackups:restoreList the import folder / check a file in it (name)
POST /api/backups/uploadsbackups:restoreStart an upload, or resume the same file's (fileName, size, lastModified, expectedSha256)
PUT /api/backups/uploads/{id}/chunks/{n}backups:restoreOne piece, with Content-Digest: sha-256=:<base64>:
GET …/uploads/{id}/status, POST …/complete, DELETE …/uploads/{id}backups:restoreProgress, finish (assembled and checked in the background), cancel