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
- 01dumpa one-off container from the engine image, credentials via env
- 02storea file on the aoox_backups volume
- 03uploadrclone to S3 if a destination is set
- 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.
| Engine | How it dumps | Restore |
|---|---|---|
| PostgreSQL | pg_dump | gzip (or pg_dumpall for all databases) | Online — streamed into psql, no downtime. |
| MySQL / MariaDB | mysqldump / mariadb-dump as root | Online — streamed into mysql. |
| Redis | redis-cli --rdb | Offline — 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
The Backup schedule & destination form
Frequency Cron Off —Every hour 0 * * * *Daily at 02:00 0 2 * * *Weekly (Sunday 03:00) 0 3 * * 0Custom 5-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.
Save
The schedule is re-registered immediately, no API restart needed. Cron runs in the API's time zone: UTC unless
TZis set in.env.dist; the form shows the zone in use.
Restore
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.
Wait for it to finish
SQL: no downtime, but
DROP/CREATEstatements 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.
| Action | What happens |
|---|---|
| Backup | A one-off busybox container, the volume mounted :ro, tar czf into <app>/<mount>/<stamp>.tar.gz. The application container keeps running. |
| Restore | Stop the container → clear the volume → extract → start. There's brief downtime. |
| Schedule | The 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_backupsvolume 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.
| Action | Detail |
|---|---|
| Backup now | Creates a snapshot immediately; it appears in the list with its size and time. |
| Schedule | A 5-field cron + an optional S3 destination, same as database backups. |
| Download | Save the file off-server — this is what's used when reinstalling. |
| Restore from file | Upload a backup file; every table is rewritten in a single transaction, then the scheduler and SSH sessions are rebuilt. |
- A file can only be restored to the same schema version — restoring to a much different aoox version is rejected.
ENCRYPTION_KEYmust 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
Integrations → Backup destinations → New backup destination (owner/admin)
Field Detail Name A label. Endpoint Empty = AWS S3. Fill in for MinIO, Wasabi, R2, etc. — e.g. https://minio.example.com, orhttp://<stack container>:9000for an S3 server such as RustFS running inside aoox (see the callout below).Region Required for AWS ( us-east-1); other providers usually don't care.Bucket Must already exist — aoox doesn't create it. Prefix Optional, e.g. aoox/prod.Access key ID / Secret access key The secret is shown only once and stored encrypted. Path-style On by default (MinIO); turn off for virtual-host style (AWS, R2). Test connection
Runs
rclone lsjsonagainst the bucket via therclone/rclonecontainer; provider errors are shown as-is.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.
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.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
| Need | Settings |
|---|---|
| Small production | Daily at 02:00, keep 7, S3 destination. Test a restore to a new database once a month. |
| Frequently changing data | Every hour, keep 24–48, S3 destination. |
| Before a big migration/import | A manual backup (never pruned) — name it in your team notes. |
| Moving servers / disaster recovery | Instance + database + volume backups, all with an S3 destination; also keep a copy of .env.dist (JWT_SECRET & ENCRYPTION_KEY). |
| Applications with uploads | Daily volume backup + a database backup at the same time. |
Common pitfalls
| Symptom | Cause & fix |
|---|---|
| A 20-byte / empty backup | Already 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: NoSuchBucket | The 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 change | A 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 full | Lower the keep count, delete old manual backups, or move to S3 and delete locally. |
Next steps
Found a mistake? Edit this page on GitHub ↗