$ aoox

DocsStacks & databases

Backup & restore

Back up managed databases and application volumes: manual or scheduled, automatic retention, S3 copies, and restore.

What
Databases, application volumes, and the panel itself
When
Manual, or a cron schedule with retention
Where
The aoox_backups volume + optional S3

One backup's flow

  1. 01dumpa one-off container from the engine image, credentials via env
  2. 02storea file on the aoox_backups volume
  3. 03uploadrclone to S3 if a destination is set
  4. 04prunethe oldest scheduled backup past retention is removed (local + S3)

Files never pass through the API process — dump, upload, and restore are all done by one-off containers. Manual backups are never pruned by retention; only scheduled ones are.

Database backups

Database page → Backup tab.

EngineHow it dumpsRestore
PostgreSQLpg_dump | gzip (or pg_dumpall for all databases)Online — streamed into psql, no downtime.
MySQL / MariaDBmysqldump / mariadb-dump as rootOnline — streamed into mysql.
Redisredis-cli --rdbOffline — the container is stopped, the snapshot is mounted as the base AOF, then it's started again.

Manual backup

Click Backup. A new row appears with its status, size, time, trigger (manual/scheduled), an S3 badge if copied over (or S3 only if the local file is already gone), and download / restore / delete buttons.

Schedule & destination

  1. The Backup schedule & destination form

    FrequencyCron
    Off—
    Every hour0 * * * *
    Daily at 02:000 2 * * *
    Weekly (Sunday 03:00)0 3 * * 0
    Custom5-field cron, e.g. 30 1 * * *
    • Keep count — defaults to 7; the oldest scheduled backup beyond this count is removed after the next one runs.
    • S3 destination — Local only, or one of the registered destinations (see below).
    • All databases on the server — for PostgreSQL/MySQL/MariaDB with extra databases: the dump covers all of them (PostgreSQL uses pg_dumpall, including roles). The backup row is marked all databases.
  2. Save

    The schedule is re-registered immediately, no API restart needed. Cron runs in the API's time zone: UTC unless TZ is set in .env.dist; the form shows the zone in use.

Restore

  1. Click restore on a backup row → confirm

    If the local file is already gone (pruned by retention, a new disk) but an S3 copy still exists, the row is marked S3 only — restore and download still work: the file is pulled back from S3 automatically first. For applications on a remote server, that pull happens on that server's daemon.

  2. Wait for it to finish

    SQL: no downtime, but DROP/CREATE statements in the dump will replace table contents. Redis: the container stops briefly.

Application volume backups

Application page → Mount tab → the Volume backup section. Applies only to volume-type mounts (bind mounts aren't supported yet). If the application has more than one volume, pick it first.

ActionWhat happens
BackupA one-off busybox container, the volume mounted :ro, tar czf into <app>/<mount>/<stamp>.tar.gz. The application container keeps running.
RestoreStop the container → clear the volume → extract → start. There's brief downtime.
ScheduleThe Volume backup schedule form: frequency, keep count, S3 destination — applies to all of an application's volumes; pruning is per volume.
  • Also works for applications on a remote server — backup, S3 upload/download, and restore run on that server's daemon.
  • Backing up while the app is writing can capture a half-written file — schedule it during quiet hours.
  • Deleting a database (Delete with data) or a project (Also delete data) also deletes its database and volume backup files on the server and their S3 copies. Without that option the files are kept as a safety net, but they stay in the aoox_backups volume and are no longer listed in the dashboard; remove them by hand when you no longer need them.

Instance backup (the panel itself)

Settings → Instance backup (owner) saves aoox's own internal database: users, projects, applications, databases, schedules, encrypted credentials — all as a single gzipped JSON file on the backup volume, without pg_dump.

ActionDetail
Backup nowCreates a snapshot immediately; it appears in the list with its size and time.
ScheduleA 5-field cron + an optional S3 destination, same as database backups.
DownloadSave the file off-server — this is what's used when reinstalling.
Restore from fileUpload a backup file; every table is rewritten in a single transaction, then the scheduler and SSH sessions are rebuilt.
Things to watch out for when restoring
  • A file can only be restored to the same schema version — restoring to a much different aoox version is rejected.
  • ENCRYPTION_KEY must match what it was when the backup was made; if it differs, stored credentials can't be read and aoox reports it.
  • This restores the panel, not application data. Containers, volumes, and images remain the job of the database/volume backups.

For moving to a new server: instance backup + database backup + volume backup, all with an S3 destination. A lighter alternative for moving just one project: export/import project.

S3 destinations

  1. Integrations → Backup destinations → New backup destination (owner/admin)

    FieldDetail
    NameA label.
    EndpointEmpty = AWS S3. Fill in for MinIO, Wasabi, R2, etc. — e.g. https://minio.example.com, or http://<stack container>:9000 for an S3 server such as RustFS running inside aoox (see the callout below).
    RegionRequired for AWS (us-east-1); other providers usually don't care.
    BucketMust already exist — aoox doesn't create it.
    PrefixOptional, e.g. aoox/prod.
    Access key ID / Secret access keyThe secret is shown only once and stored encrypted.
    Path-styleOn by default (MinIO); turn off for virtual-host style (AWS, R2).
  2. Test connection

    Runs rclone lsjson against the bucket via the rclone/rclone container; provider errors are shown as-is.

  3. Pick it in a database / volume backup schedule

    Every backup (manual or scheduled) that succeeds is uploaded to <prefix>/<slug>/<stamp>.<ext>. Deleting/pruning a backup also removes the remote object.

Using RustFS (or any stack) as the destination
The upload runs in a helper container on the aoox network, so the S3 server must be reachable from there. A compose stack only joins that network when at least one of its services has a domain: give the RustFS stack a domain on any service (the console is a good choice), then use http://<container name>:9000 as the endpoint (the name is on the stack page, aoox-<stack slug>-rustfs-1), with path-style on and the bucket created first. Don't rely on a host port bound to 127.0.0.1: containers can't reach it. A public host port works through http://<server IP>:9000, but it exposes S3 to the internet.
A failed upload leaves the backup as failed and triggers a notification — the local file is still there to download. The whole point of a destination is having a copy elsewhere, so failures aren't hidden.

Suggested strategies

NeedSettings
Small productionDaily at 02:00, keep 7, S3 destination. Test a restore to a new database once a month.
Frequently changing dataEvery hour, keep 24–48, S3 destination.
Before a big migration/importA manual backup (never pruned) — name it in your team notes.
Moving servers / disaster recoveryInstance + database + volume backups, all with an S3 destination; also keep a copy of .env.dist (JWT_SECRET & ENCRYPTION_KEY).
Applications with uploadsDaily volume backup + a database backup at the same time.

Common pitfalls

SymptomCause & fix
A 20-byte / empty backupAlready handled (the dump's exit status is propagated). If it happens, check the backup's error message — usually credentials or a database that wasn't ready.
S3 destination test fails: NoSuchBucketThe bucket hasn't been created, or the region is wrong (AWS).
Test fails against a local S3 server (RustFS, MinIO)The endpoint is using localhost from inside the container, or the stack has no domain so it isn't on the aoox network. Give the stack a domain on any service, then use the container name + port 9000, or its public domain.
Redis restore: data doesn't changeA Redis 7 backup is mounted as the base AOF; make sure the container was actually restarted (status back to running) and nothing wrote to it before the restore finished.
Backup volume is fullLower the keep count, delete old manual backups, or move to S3 and delete locally.
Not available yet
Separate retention for S3 objects; bind mount backups; backups for compose stacks & the databases inside them; cross-schema-version instance restore.

Next steps

Found a mistake? Edit this page on GitHub ↗