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.

A practical guide for DigitalOcean, Aiven, Supabase, Scaleway, RDS and other managed Postgres/MySQL. Works even if you have never used dbferry.

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:

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 catch: this only works to the granularity of the provider's snapshots (often nightly) and their retention window (often ~7 days). If the bad write landed this afternoon and the last snapshot was midnight, you lose everything since midnight. And every recovery starts by paying for a full second cluster.

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