Backup and restore
Where the nightly MariaDB dumps land, how long they are kept, and the two restore paths — a scratch container for verification, and managed recovery from bench console.
Where BenchPress's automatic MariaDB dumps live, and how to turn one back into
a database.
Who this is for. Whoever owns the host, and only them. There is no button
and no API for a restore, on purpose.
Before you start.
A restore overwrites every database on the target server. A dump made by
BenchPress contains all databases on the shared MariaDB, so restoring it
replaces every bench site at once. Never point a restore at a live server
unless you have accepted losing what is on it. Restore to a scratch container
instead.
Steps
Pick the path that matches what you are doing.
| Path | Use it to | Risk |
|---|---|---|
| A, a scratch container | verify a dump, or read old data | none. It touches nothing BenchPress manages |
| B, managed recovery | rebuild a server after a real loss | destroys the target server's current data |
Where backups live
Every Active Database Server is dumped nightly at 02:00 server time. The
last 7 dumps are kept.
| Location | Path | Written |
|---|---|---|
| Host disk (primary) | sites/<site>/private/backups/mariadb/all_databases_<timestamp>.sql.gz |
after every successful backup. It is copied off the database volume, so losing the volume does not lose the backups |
| Inside the container (fallback) | /var/lib/mysql/backups/ in benchpress-mariadb |
only when the copy to host disk failed. Pruned on the same 7-dump retention |
To take a dump outside the schedule, run it from bench console:
from benchpress.mariadb_manager import backup_database_server
backup_database_server("<database-server-name>") # returns the host path
Only the shared MariaDB is backed up. Other bench data in Docker volumes
is not. Take your own copies of anything that matters. See
Production safety.
Path A, a scratch container
Loads a dump into a throwaway MariaDB. Nothing BenchPress manages is touched.
-
Start a scratch container on the same major version as the source:
docker run --name scratch-restore -e MARIADB_ROOT_PASSWORD=scratch -d mariadb:10.6 -
Wait until it accepts connections:
docker exec scratch-restore mariadb -u root -pscratch -e "SELECT 1" -
Copy the dump in and load it:
docker cp sites/<site>/private/backups/mariadb/<dump>.sql.gz scratch-restore:/tmp/ docker exec scratch-restore bash -c "gunzip -c /tmp/<dump>.sql.gz | mariadb -u root -pscratch" -
Run the post-restore check.
-
Throw the container away:
docker rm -f scratch-restore
Path B, managed recovery
Loads a host-side dump into a Database Server that BenchPress manages. Use
this only after a real loss.
This overwrites all databases on the target server. There is deliberately
no UI and no API for it. A human runs it in bench console.
-
Open a console on the host bench:
bench --site <site> console -
Run the helper with the document name and the host dump path:
from benchpress.mariadb_manager import restore_database_server restore_database_server( "<database-server-name>", "sites/<site>/private/backups/mariadb/<dump>.sql.gz", )It pushes the dump into the container, pipes it through
gunzip -c | mariadb -u root, and raises on any non-zero exit. -
Run the post-restore check.
-
Restart the benches that were using the server, so they reconnect cleanly.
Post-restore check
Confirm real data survived the round trip. Pick a table you know had rows —
any site's tabDefaultValue will do — and select from it.
docker exec <container> mariadb -u root -p<password> -e \
"SELECT defkey FROM <database>.tabDefaultValue LIMIT 1"
A known row must come back. An empty result or an unknown-table error means
the restore did not load what you expected. Do not put the server back into
service.
Verify
Check the schedule is actually running, not just configured. A backup nobody
watches is not a backup.
ls -lh sites/<site>/private/backups/mariadb/ | tail -8
You should see up to seven dumps, the newest from last night. If the newest is
older than that, the scheduled job is not running — see
Diagnostics.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| No dumps on host disk | The nightly job has never run, or the scheduler is stopped | Check bench --site <site> doctor, then run backup_database_server by hand |
| Dumps exist only inside the container | The copy to host disk failed | Fix the disk or the permission, then re-run the backup. The in-container copy is the fallback, not the plan |
| More than 7 dumps | Retention did not prune | Harmless. Delete the oldest by hand |
| A restore ran and a site still fails | The site's MariaDB user was not in the dump, or its password changed | Redeploy that bench. Deploy owns the user |
restore_database_server raises immediately |
The path is wrong, or the file is not readable by the bench user | Use the full path the backup helper returned |
| The restore finished and the check returns nothing | The dump did not hold what you expected | Do not put the server back into service. Try an older dump |
Reference
| Item | Value |
|---|---|
| Schedule | daily, 0 2 * * * server time |
| Job | benchpress.mariadb_manager.enqueue_backup |
| Retention | 7 dumps |
| Scope | all databases on the server |
| Host path | sites/<site>/private/backups/mariadb/all_databases_<timestamp>.sql.gz |
| Container fallback | /var/lib/mysql/backups/ |
| Manual backup | benchpress.mariadb_manager.backup_database_server |
| Manual restore | benchpress.mariadb_manager.restore_database_server |
| Backup timeout | 3600 s |
Related
- The shared database server — the record these dumps come from.
- Upgrading — the pre-upgrade backup gate for the control-plane site.
- Production safety — the full list of what is not backed up.