DEV Community

MKP Digital
MKP Digital

Posted on

I thought backing up Supabase was just pg_dump + rclone. It wasn't.

A few days ago somebody on Reddit asked how to make proper backups of a Supabase project.

My first answer was pretty straightforward.

Dump the database with the Supabase CLI. Copy the actual Storage objects somewhere else. Put both jobs in cron or GitHub Actions. Done.

Or at least that's what I thought.

Then people started pointing out the uncomfortable parts.

A database dump and a Storage copy are two separate operations. An object can be added, deleted or replaced between them.

A backup job completing successfully doesn't prove the result can actually be restored.

There's also more to a Supabase project than the public schema. Auth matters. Roles matter. Storage metadata matters. Edge Functions matter. Project configuration matters.

And some platform state simply isn't exposed in a form that can be backed up and restored identically.

At that point I made the mistake of writing:

Give me a couple of days. I've decided to build exactly this now.

So I did.

pgDumpster

pgDumpster is a CLI for backing up, inspecting, verifying and restoring hosted Supabase projects.

The basic idea is that a backup should be more than a directory containing a .sql file and some Storage objects.

Each backup is treated as a portable archive with a manifest describing what was captured, integrity information and explicit coverage information.

It currently deals with:

PostgreSQL roles, schema and data
Storage buckets, metadata and actual objects
Auth/API-related state where Supabase exposes it
Edge Functions and their recoverable configuration
relevant project/control-plane configuration where there is a documented read/write path
integrity verification
age encryption
S3-compatible backup storage
restore planning and dry-runs

The restore side ended up being just as important as the backup side.

Before applying anything, pgDumpster builds a restore plan and checks the target. Things that cannot safely be restored are supposed to fail explicitly rather than being quietly ignored.

The annoying consistency problem

Probably the most interesting part of building this was accepting that there is no single atomic "Supabase snapshot".

PostgreSQL, Storage and the control plane aren't one transactional system.

If the database snapshot says an object exists but that object changes before Storage is copied, both backup jobs can individually succeed while the combined backup represents something that never actually existed at one point in time.

pgDumpster can't magically make Supabase atomic either.

Instead it tries to make those boundaries visible and verifiable rather than pretending they don't exist.

That also means the tool is deliberately quite conservative about claims like "complete backup".

If Supabase doesn't expose something, or if it can't be recreated identically on a fresh project, that belongs in the backup result as a limitation.

Restore testing matters

One comment in the original Reddit discussion made another good point: a backup you haven't restored is still partly a theory.

So the intended workflow isn't just:

backup -> success

It's closer to:

backup -> verify -> inspect -> restore plan -> fresh target -> restore -> verify

I'm still working on that side and I expect real projects to expose cases I haven't accounted for.

This is only v0.1.2.

Try it
npm install -g pgdumpster

GitHub:
https://github.com/Ciroc0/pgdumpster

Website:
https://www.pgdumpster.com/

npm:
https://www.npmjs.com/package/pgdumpster

The source is public under PolyForm Shield. It's free for normal/self-hosted use, but it's intentionally source-available rather than OSI open source because I don't want somebody wrapping it into a competing hosted backup service.

If you run Supabase in production and find an assumption I've got wrong, that's probably the most useful feedback I can get at this stage.

Top comments (0)