Guide
Restore one database from a managed cluster backup
Your managed provider took a backup of the whole cluster. You need one database back — ideally at a point in time, ideally without disturbing the other tenants. Here is how to do that by hand, and why per-database logical backups turn it into a single command.
Why "restore one database" is hard on managed databases
Managed Postgres and MySQL providers back up the whole cluster as one physical unit. Their restore flow gives you exactly one lever: spin up a new cluster from the snapshot (or from point-in-time WAL). You cannot:
- restore a single database into the running cluster,
- download the backup as a file, or
- keep the other databases untouched while you roll one back.
That is fine for a single-database app. It is painful the moment one cluster holds many databases — a multi-tenant SaaS with a database per tenant, or an agency hosting client_a, client_b, client_c side by side. One corrupted tenant should not mean rolling everyone back to last night.
The manual way: restore the cluster, extract one database
Without per-database backups, the only route is to reconstruct the whole cluster somewhere temporary, pull the one database out, and load it where you need it. Concretely:
1. Restore the provider snapshot to a throwaway cluster
In your provider's console, restore the backup (or a point in time) to a new cluster. This is a full-size copy — same plan, same cost, running until you delete it. Depending on data size this takes minutes to hours, and you pay for the instance the whole time.
2. Dump just the database you need
Once the temporary cluster is up, connect to it and dump the single database with a logical tool. For Postgres:
# from the temporary restored cluster → a local custom-format dump
pg_dump --format=custom --no-owner --no-privileges \
"postgresql://doadmin:PASS@temp-cluster:25060/client_b?sslmode=require" \
-f client_b.dump
For MySQL, the equivalent is a single-schema mysqldump:
mysqldump --single-transaction --set-gtid-purged=OFF \
-h temp-cluster -P 25060 -u doadmin -p \
client_b > client_b.sql
3. Restore that database where you actually want it
Load the dump into your live cluster — under the original name, or under a new one so you can inspect before cutting over. Restoring alongside is the safe move:
# create a side database and restore into it — nothing else is touched
createdb -h live-cluster -p 25060 -U doadmin client_b_restored
pg_restore --no-owner --no-privileges --dbname \
"postgresql://doadmin:PASS@live-cluster:25060/client_b_restored?sslmode=require" \
client_b.dump
4. Delete the temporary cluster
Don't forget this step — the throwaway cluster keeps billing until you tear it down.
The better way: per-database logical backups you own
If instead you already have a logical dump of each database, taken on a schedule and stored in your own bucket, restoring one database is just decrypt → decompress → restore. No temporary cluster, no touching the other tenants:
# one database, straight from your own S3 bucket, into a side database
aws s3 cp s3://my-bucket/client_b/2026/07/22-0300.dump.zst.age - \
| age --decrypt -i ~/.dbferry/key.txt \
| zstd -d \
| pg_restore --no-owner --dbname "postgresql://…/client_b_restored"
Because each database is dumped separately, you restore one, not everything — and because the dumps are portable logical files, you can load them into a different provider, a local Postgres, or a staging box just as easily.
dbferry makes this the default
dbferry backs up every database on your managed cluster separately, on a schedule, streamed straight into your own S3-compatible bucket with client-side encryption — so a single-database restore is always one command away. Zero data retention: the key is yours, we never see your data.
Practical notes
- Restore alongside, then cut over. Loading into
client_b_restoredlets you diff and verify before renaming or repointing the app. A restore you didn't verify is a hope, not a backup. - Least privilege for the dump. A backup role needs only
CONNECTplusSELECT(andUSAGEon schemas/sequences) — never a superuser. Managed providers gate physical access anyway, which is exactly why logical dumps are the realistic path. - Watch the retention window. Provider snapshots expire; your own bucket keeps whatever GFS policy you choose (e.g. 7 daily, 4 weekly, 12 monthly).