Restore a Supabase backup
If you’re here on a bad day: your backups live in your own bucket, in plain formats, so every path on this page works with or without BackupDrill in the loop. Each snapshot sits at <prefix>/<project>/<timestamp>/ and contains dump.pgcustom, a storage/ tree with your files, a checksummed manifest.json — and, from CLI 2.0 on, a RECOVERY.md runbook written for that exact snapshot: its object keys, its checksums, its extension list. Even if this website is unreachable, your bucket explains how to recover.
First, the Supabase gotcha
Restoring the database alone does not bring back your Storage files. A database restore only recovers storage.objects — the metadata — while the actual files live in a separate object store. That is exactly why BackupDrill snapshots include the files themselves under storage/: a full recovery is the database restore plus putting those files back.
Path A — the console recovery wizard
If you have a BackupDrill account, this is the shortest route. Open the project, hit Restore this project, and the wizard walks four steps: pick a restore point, run a read-only preflight that writes nothing to the target, read what the preflight found, then re-type the target project ref and tick the safety confirmations before anything executes. The target has to be a fresh, empty Supabase project — never the damaged one.
Two limits. The wizard restores the database only; for Storage files it hands you the equivalent CLI command (path C below), which restores database and files in one run when the snapshot contains them. And one-click execution is Solo and above — Free still runs the read-only preflight and exports the exact commands. Auth users, secret values and Edge Functions are in no snapshot; reconfigure them by hand whichever path you take.
Your target credentials are sealed in your browser; the console and our database only ever hold ciphertext, the worker opens them in memory for the job, and they are deleted the moment it reaches a terminal state.
Path B — you hold the artifacts
Because backups are written to your bucket, recovery never depends on our service being up, or on you still being a customer. Find the snapshot you want by timestamp in your bucket, then use the CLI (path C) or plain pg_restore(path D) against it. If you’re a hosted customer and want a human on the line, email support@backupdrill.com.
Path C — the CLI restore command (2.0+)
With the CLI configured for your bucket, restore recovers into a fresh Supabase project. The database goes through the same engine scheduled drills use — checksum-verified archive, empty target enforced, extensions pre-installed, tables verified after. Storage buckets are rebuilt with their recorded attributes, every file uploaded back and reconciled. Credentials are asked at hidden prompts — nothing lands in shell history; CI can inject BACKUPDRILL_TARGET_DATABASE_URL and BACKUPDRILL_TARGET_SERVICE_ROLE_KEY as managed secrets. Start with --dry-run, then confirm by typing the target’s project ref:
TARGET_REF="abcdefghijklmnop" # the target project's ref
backupdrill restore --database \
--target-supabase-url "https://$TARGET_REF.supabase.co" --dry-run
backupdrill restore --database \
--target-supabase-url "https://$TARGET_REF.supabase.co" \
--confirm-target "$TARGET_REF"Prefer the files on disk instead of uploading them? Skip --target-supabase-url and pass --storage-dir ./recovered.
Path D — raw pg_restore
No CLI, no dependencies beyond Postgres client tools: download dump.pgcustom from the snapshot folder and restore it by hand into an emptyproject. The snapshot’s own RECOVERY.md carries this exact command with the real object keys and the extension pre-install SQL filled in.
read -rs PGPASSWORD && export PGPASSWORD # hidden prompt
pg_restore --no-owner --no-privileges \
--dbname "postgresql://postgres.<target-ref>@<pooler-host>:5432/postgres" dump.pgcustom
unset PGPASSWORDOne error is expected and harmless: schema "public" already exists — the target always has it. Do not use --clean against a Supabase project: dropping and recreating publicsilently wipes Supabase’s default grants for anon/authenticated, and the restored API answers 401. And remember the gotcha above: this restores the database only — your Storage files are sitting next to the dump under storage/.
Why this page should be boring
Restore drills exist precisely so this page holds no surprises. On the hosted service, every drill restores your latest snapshot into a throwaway Postgres and verifies the archive sha256, that pg_restorecompletes with post-data objects applied, and that table counts match with no missing or unexpectedly empty tables — with an email the moment one fails. If you haven’t set that up yet, the quickstart takes six steps.