Docs menuBackups and restore

Daily database backups and restore on your VPS

ox backs up your app's databases every day with nothing to turn on. Back up now, keep copies in your S3 bucket, download a backup, and restore it safely.

Last updated 2026-10-08

ox backs up your app's databases every day, soon after 03:00 UTC, with nothing to turn on: PostgreSQL with pg_dump, and SQLite and a project's own Redis or MySQL as files. Each backup is kept on your server, and a copy goes to your own S3 bucket when you add one. You can also back up now, download a backup, and restore from one with ox restore.

Turn on daily backups

There is no switch: backups are on for every service that can be backed up, from its first deploy.

  • PostgreSQL is backed up with pg_dump while it keeps running, whether the database is shared or only for this project.
  • Redis, Neo4j, Qdrant and MySQL are backed up as files when they run only for this project. The service stops for a moment while its files are copied, and the log says for how long. A shared one is not backed up.
  • A custom service is backed up as files too. Set backup = false on it in ox.toml to skip it (custom services).
  • SQLite files in the project's data folder are backed up as well.

The daily backup runs once a day, soon after 03:00 UTC. Each one is kept on your server. To keep a copy off the server too, add an S3 bucket in Settings, or from a terminal:

Send backups to S3 as well
ox settings s3 --endpoint https://<account>.r2.cloudflarestorage.com --bucket ox-backups --access-key <key-id> --from-file secret.txt

--from-file reads the secret key from a file, or from standard input with -, so it never sits on the command line. Without S3, the backup log says no S3 target in Settings; the backup stays on this host.

How many backups ox keeps

KindOn your serverIn S3
Daily7, or 2 once S3 has a copy30, daily and manual together
Manual (Back up now)330, daily and manual together
Before migrate3Not sent to S3

The counts are per service. One project's backups may take up to 20% of the server's disk; past that, ox deletes the oldest ones first, but it always keeps the newest backup of each service.

A before migrate backup is the copy ox makes of each PostgreSQL database that has tables, right before a deploy runs its migrate step. It is the one to restore when a rollback needs the old tables back.

Run a database backup now

In the dashboard, open the project's Backups tab and press Back up now on the service. From a terminal, name the service entry as it is in ox.toml:

Back up now
ox backups shop now postgres --wait

Without --wait, it prints Started backups on shop: run <id> and the command to read its log.

List and download backups

The Backups tab lists each backup with its time, size and kind, and a Download button. From a terminal:

List and download
ox backups shop
ox backups shop download postgres backup:daily-20261007T030512Z.dump

The SOURCE column of the list is what the other commands take:

  • backup:<file> is a backup on your server.
  • s3:<key> is a copy in your S3 bucket.
  • trash:<time>/<file> is from a deleted project of the same name, kept in the trash for 7 days.

A download prints Saved <file> with the size. Choose the file with --output; ox never writes over a file that exists.

Restore a database from a backup

A restore replaces the service's data with the backup. Anything written after the backup was made is replaced.

In the dashboard, press Restore on a backup's row. It asks Replace postgres's data with this backup? Tick Replace postgres's data and press Restore. To restore from a dump on your computer instead, use Restore postgres from a file: pick the file, tick Replace postgres's data with this file and press Upload and restore. It takes up to 95 MB. ox loads a PostgreSQL dump into a copy first, so the live database changes only when the load works.

From a terminal, type the project's name again after --confirm:

Restore
ox restore shop postgres --from backup:daily-20261007T030512Z.dump --confirm shop --wait
ox restore shop postgres --file shop.dump --confirm shop --wait

When it is done, the log says restored postgres from <backup>. ox then runs your migrate step and restarts the app.

What ox keeps in case you change your mind

  • PostgreSQL: the app keeps running, but its open database connections are closed. The old database is kept under a new name until the next daily backup, and the log says the replaced database is kept as <name> until the next daily backup.
  • File-backed services: the service stops during the restore. The old files are kept beside the new ones until the next daily backup, and if the service does not come back healthy, ox puts them back.
  • SQLite: ox stops the app, its workers and cron jobs, and makes a manual backup first.

If a backup or restore does not work

  • restoring replaces the data of postgres in shop; to go ahead retype its name: --confirm shop: add --confirm shop.
  • --confirm "shp" does not match "shop"; nothing was done: type the project's name exactly.
  • The dashboard says tick the box to confirm that the restore replaces postgres's data: tick the box, then press Restore again.
  • The disk is too small for the restore: the message says how much is free and how much it needs. Free some space, then restore again. Disk full has more.
  • A service has no backups: a shared Redis, Neo4j, Qdrant or MySQL is not backed up, and a custom service with backup = false is skipped.