A disaster recovery runbook for Supabase
A disaster recovery plan is a written answer to one question: when this database is gone or wrong, what exactly do we do? A runbook is that plan made executable — failure scenarios mapped to recovery paths, each with the preconditions it needs, owned by a named person, and rehearsed before it is needed. Supabase gives you real building blocks — daily backups on paid plans, a point-in-time recovery add-on — but building blocks are not a plan: the official backups skip Storage files, live inside the platform they protect, and are never restore-tested. This guide is the missing document: recovery objectives in Supabase terms, a failure-to-path matrix, a three-phase runbook, and a checklist you can pin to the wall.
RPO and RTO, in Supabase terms
Every disaster recovery conversation runs on two numbers. Pin them down first, because they decide everything else you buy or build.
RPO — how much data you can afford to lose
The Recovery Point Objective is the maximum gap, measured in time, between the last recoverable state and the moment of failure. In Supabase terms (numbers checked against the official docs, July 2026): daily backups on the Pro plan and above mean a worst case of roughly 24 hours of lost writes. The PITR add-on archives WAL every two minutes, for a worst-case RPO of two minutes at $100/month per project — if you genuinely need sub-24-hour recovery points, PITR is the right tool and nothing snapshot-based substitutes for it. An external backup’s RPO is simply its schedule: daily snapshots, 24-hour worst case. And the Free plan includes no automated backups at all — its RPO is undefined until you set something up.
RTO — how long recovery takes
The Recovery Time Objective is how long the service may stay down while you recover. Supabase does not publish one, for an honest reason: restore duration depends on database size, and the project is offline for the duration of an in-place restore. Which leads to the uncomfortable rule this whole guide keeps returning to: you do not know your RTO until you have restored a real backup and timed it. A restore drill produces that number as a side effect — the wall-clock restore time of your latest snapshot, measured on a schedule instead of during an outage.
Failure scenarios and the path back
Two facts shape every row of this table: the official backups cover the database only — never Storage files — and they live inside the same platform and account they protect. Scenarios where those two facts bite are the ones where only an external copy saves you.
| Failure | What can save you | Recovery path | Preconditions and limits |
|---|---|---|---|
| Bad DELETE or UPDATE | PITR (best), a daily backup, or an external snapshot | With PITR: restore to the minute before the statement ran. Without it: restore the latest snapshot into a separate database, extract the affected rows, and re-insert them. | PITR must have been enabled before the incident ($100/month per project). A full in-place restore of a daily backup also rolls back every write since the backup — up to 24 hours of them. |
| Bad migration | Any backup taken before the migration ran — a pre-deploy snapshot is the ideal one | Restore the pre-migration state into a staging database, verify it, then either fix the migration and roll forward, or restore over production. | You need the migration's timestamp to pick the recovery point, and an in-place restore discards every write made after the backup — which is why the runbook below snapshots before every migration. |
| Project paused (Free plan) | Supabase itself — no backup needed inside the window | Restore from the dashboard. After the window: download the backup and Storage files from the dashboard and migrate them to a new project. | Free projects pause after about a week of inactivity; the in-place restore window is 90 days (checked July 2026). Paid-plan projects are not auto-paused. |
| Project deleted | Only a copy outside Supabase | Create a new project, pg_restore the database dump, upload the Storage files, rotate connection strings and keys in your app. | Deletion is permanent and irreversible — Supabase's own backups and PITR snapshots become inaccessible with the project. If no off-platform copy exists, there is no recovery path. |
| Storage files deleted | Only an external file backup | Copy the files back from your backup bucket, then reconcile storage.objects metadata against what actually exists. | No Supabase backup tier covers Storage files — database backups and PITR carry only storage.objects metadata, so a database restore returns pointers to files that no longer exist. The file backup must predate the deletion. |
| Leaked credentials | A backup from before the compromise — ideally one the leaked credentials cannot touch | Rotate first — database password, API keys, JWT secret. Then bound the tampering window from logs, and diff or restore against a pre-compromise snapshot in staging before trusting the data. | An attacker with dashboard access can delete the project, and the built-in backups die with it. Copies in a bucket you own, under separate credentials, are the ones a Supabase-side compromise cannot reach. |
| Platform-level incident | Patience, usually — or an off-platform copy if you cannot wait | For most outages, waiting for Supabase to recover is the right call. If the business cannot wait: stand up any Postgres, pg_restore your external snapshot, and serve degraded. | Built-in backups and PITR live inside the platform, so they are unavailable exactly when the platform is. An emergency migration recovers your data, not your stack — Auth, Storage URLs, and Edge Functions do not come along with pg_restore. |
Where an “external backup” appears above, it means a scheduled copy in a bucket you own — DIY pg_dump (the backup options guide has working commands), a GitHub Actions workflow, or a service like BackupDrill that also backs up Storage files and restore-tests the result. What matters for the matrix is not which tool — it is that the copy exists outside the platform, includes the files, and has been proven to restore.
The runbook, part 1 — before anything breaks
Everything in this section is cheap on a calm Tuesday and expensive to discover missing mid-incident.
- Backup inventory. Write down, per project: the plan tier and what it implies (daily backups from Pro, nothing automated on Free), whether PITR is enabled, where external backups land and on what schedule, whether Storage files are covered, and what the dump actually contains (the
publicschema by default — notauth, notstoragemetadata). - Access and ownership. Name an owner and a deputy. Verify both can log into the Supabase dashboard, both can reach the backup bucket, and both know where this runbook lives — somewhere readable while your own infrastructure is down.
- Pre-migration snapshots. Make “snapshot before migrating” a step in the deploy process, not a habit in someone’s head. The bad-migration row of the matrix is only survivable if this happened.
- Drills on a schedule. The restore testing guide covers the how — manually in Docker, with the open-source CLI, or hosted on a schedule. Weekly for production is the default worth arguing yourself out of; each drill also measures the restore time that becomes your real RTO figure.
Part 2 — during the incident
The failure mode of smart people in an outage is skipping to step five. The order below exists because each step protects the ones after it.
1. Contain the damage
Stop whatever is making it worse before touching backups: pause the deploy, disable the failing job, cut the application’s write path if a bad process is still running. Then — counterintuitive but critical — take a fresh backup of the damaged database. A restore overwrites the current state; if the recovery goes wrong, the damaged state you destroyed still contained every write since the last good snapshot.
2. Establish the scope
Which tables, which files, since when? The damage timestamp decides your recovery point — restoring to yesterday when the corruption started last week recovers corrupted data with extra steps. The damage surface decides the intervention: three deleted rows call for surgical recovery, not a full restore that rolls the whole database back a day.
3. Choose the recovery path
This is what the matrix above is for — match the failure, check the preconditions column honestly. The general rule: prefer the smallest intervention that recovers the data. PITR beats a snapshot for a recent bad write; extracting rows from a restored copy beats an in-place restore; an in-place restore beats migrating to a new project.
4. Verify in a disposable environment first
Restore into a fresh database or a throwaway Postgres and check what came back — table counts, the tables that must never be empty, the recency of an append-heavy table — before production sees any of it. The restore testing guide is this exact loop with working commands. Never point production at unverified data: one recovery is an incident, a bad recovery on top of it is a crisis.
5. Switch over and confirm
Apply the verified recovery — in-place restore, surgical re-insert, or a new project with pg_restore and a file copy — then run application-level checks, the queries your app actually issues. Tell your users what happened and what window of data, if any, was lost. The RPO you wrote down in part 1 is what makes that sentence sayable.
Part 3 — after: the postmortem
- Reconstruct the timeline — detection, decision, recovery — and compare the RPO and RTO you actually achieved against the numbers you assumed. The gap between them is the most useful output of the whole incident.
- Fix the class, not the instance. If a bad migration burned you, the fix is a pre-migration snapshot step in the deploy process — not a resolution to be more careful.
- Add the check that would have caught it earlier — a drill, an alert, a checklist line. Every incident should make the next one shorter.
- Update this runbook the same week, while the wrong assumptions are still fresh. A runbook that survived an incident unchanged was not read during it.
The checklist
Fifteen lines. Print it, pin it, or paste it into the incident channel topic. Every unchecked box is a scenario in the matrix above with a worse ending.
- Automated database backups run on a schedule you chose, and you can say from memory what that schedule is.
- Storage files have their own backup — a database backup alone does not include them.
- At least one backup copy lives outside Supabase, in an account you control.
- The credentials that write backups are not the credentials that run production.
- Your worst-case data loss (RPO) is written down as a number of hours, and someone with authority has accepted it.
- You have restored a real backup at least once and know how long it takes (RTO).
- A restore test ran in the last month, and someone would notice if it stopped running.
- Every schema migration is preceded by a snapshot — as a deploy-process step, not a habit.
- Projects that need sub-24-hour RPO have PITR enabled — or the decision not to pay $100/month per project is recorded.
- The runbook names an owner and a deputy, and either can execute it alone.
- The runbook is reachable when your infrastructure is down.
- Dashboard access and bucket credentials are re-verified quarterly — people leave, keys rotate.
- Backup-failure alerts reach a human, not just a log file.
- Free-plan projects: someone owns the pause risk — either activity is monitored or the 90-day restore window is understood.
- A postmortem template exists, and the last incident actually produced one.
FAQ
Does Supabase have disaster recovery?
Partly. On the Pro plan and above, Supabase takes daily backups (7-day retention on Pro), and the Point-in-Time Recovery add-on at $100/month per project brings worst-case data loss down to two minutes. But those backups do not include Storage files, they are never restore-tested, and they live inside the platform — a deleted project takes its backups and PITR snapshots with it. Disaster recovery is a plan, not a feature: you still need an off-platform copy, a tested restore procedure, and a written runbook.
What is the RPO and RTO of Supabase backups?
RPO — how much data you can lose: daily backups mean a worst case of roughly 24 hours of writes; the PITR add-on archives WAL every two minutes for a worst-case RPO of two minutes; an external backup's RPO equals its schedule; the Free plan has no automated backups at all. RTO — how long recovery takes — is not a published number because it depends on your database size: Supabase restores take the project offline for the duration. The only way to know your RTO is to restore a real backup and time it.
Can I recover a deleted Supabase project?
No. Supabase documents project deletion as permanent and irreversible: the database, Storage files, automated backups, and PITR snapshots all become inaccessible. Do not confuse deleted with paused — a paused Free-plan project can be restored from the dashboard for 90 days, and even after that window its backup and Storage files can still be downloaded and migrated to a new project. The only thing that survives deletion is a copy outside Supabase, made before it happened.
How often should we test our disaster recovery plan?
Two cadences. Restore-test the backups themselves frequently — weekly for production, plus after every schema migration — because that is where silent failures accumulate, and it is cheap to automate. Walk through the full runbook less often but on a calendar — quarterly is a common choice: one person, one hour, simulating one scenario from the matrix end to end. The drill verifies the backups; the walkthrough verifies the humans, the access, and the document.
Sources
- Supabase docs — Database Backups
- Supabase — Pricing
- Supabase docs — Manage PITR usage
- Supabase docs — Project Pausing
- Supabase docs — Restore project after 90-day pause
Facts and prices last verified July 11, 2026 against the sources above. Written by the team behind BackupDrill.
A runbook is a document; what keeps it true is the drills. BackupDrill backs up the database and Storage files together into your own bucket, then restore-tests the result on a schedule — open the console (the free plan covers backups for one project) or run the open-source CLI yourself.