The backupdrill CLI

An open-source (MIT) CLI that backs up a Supabase project — the Postgres database and your Storage files — to your own S3, R2, or B2 bucket, with a checksummed manifest.json so you can prove what was captured. Backups stream from your machine or CI straight to your bucket; there are no servers of ours in the path. Source and issues at github.com/backupdrill/cli.

Requirements

Node.js ≥ 20, and a pg_dump whose major version is ≥ your Supabase Postgres version (Supabase runs PG 15–17). On macOS: brew install libpq and add it to PATH, or point BACKUPDRILL_PG_DUMP at the binary. The CLI checks this and refuses to run a mismatched dump.

Install

npm install -g backupdrill
# or run without installing:
npx backupdrill backup

Configure

Configure via env vars, a config file, or flags (precedence: flags > env > file). Use the Session pooler connection string from the Connect button at the top of the Supabase dashboard — the postgres user in it works as-is. A read-only backup_reader role is an advanced option that only works if your schema has no RLS-enabled tables — see the advanced setup in the quickstart.

export BACKUPDRILL_DATABASE_URL="postgresql://postgres.<ref>:<pw>@aws-0-<region>.pooler.supabase.com:5432/postgres"
export BACKUPDRILL_PROJECT_NAME="my-app"
export BACKUPDRILL_S3_ENDPOINT="https://<account>.r2.cloudflarestorage.com"  # omit for AWS S3
export BACKUPDRILL_S3_REGION="auto"
export BACKUPDRILL_S3_BUCKET="my-backups"
export BACKUPDRILL_S3_ACCESS_KEY_ID="…"
export BACKUPDRILL_S3_SECRET_ACCESS_KEY="…"

To also capture Storage files (a database restore alone only brings back storage.objects metadata, not the files), enable S3 access under Storage → Settings → S3 Access Keys in the Supabase dashboard and add:

export BACKUPDRILL_SUPABASE_STORAGE_ENDPOINT="https://<ref>.storage.supabase.co/storage/v1/s3"
export BACKUPDRILL_SUPABASE_STORAGE_REGION="<project-region>"
export BACKUPDRILL_SUPABASE_STORAGE_ACCESS_KEY_ID="…"
export BACKUPDRILL_SUPABASE_STORAGE_SECRET_ACCESS_KEY="…"
# optional: only specific buckets
export BACKUPDRILL_SUPABASE_STORAGE_BUCKETS="avatars,uploads"

Commands

backupdrill backup                   # dump DB (+ Storage files if configured) to your bucket

backupdrill estimate --plan pro      # project your monthly Supabase egress cost before scheduling

backupdrill drill                    # restore latest snapshot into a throwaway Postgres (needs
                                     # Docker) and verify it; --snapshot <timestamp> to pick one,
                                     # --verify-all-files to checksum every Storage file

backupdrill drill \
  --check-cmd "npm run smoke"        # v0.2: after structural checks pass, run YOUR smoke test
                                     # against the restored copy (BACKUPDRILL_SANDBOX_URL is set;
                                     # exit 0 = pass, reported as 'app checks'); --keep holds the
                                     # sandbox open on failure for inspection

backupdrill restore \
  --target-database-url "postgresql://…/postgres" \
  --storage-dir ./recovered          # real recovery: DB into a target, files to a local folder

For the full recovery playbook — including a raw pg_restore fallback — see Restore & recovery.

Egress cost — read before scheduling daily backups

A backup pulls data out of Supabase, which is uncached egress on your Supabase bill: $0.09/GB above your plan’s allowance (Free 5 GB, Pro/Team 250 GB per month). Spend Cap is on by default on Pro/Team — blow past the quota and Supabase throttles your whole project’s egress for the rest of the month and the backup fails. Free is a hard cap: a large daily backup can’t complete on it. Rough cost on Pro with Spend Cap off, backups as your only egress:

DB + StorageWeeklyDaily
10 GB$0/month (within 250 GB)~$4.50/month
50 GB~$0/month~$112/month

Cost scales with size × frequency. Run backupdrill estimate for your own numbers, and prefer weekly once your data is large.

Schedule it with GitHub Actions

The repo ships a workflow template at examples/scheduled-backup.yml — copy it into your own repo as .github/workflows/scheduled-backup.yml. It runs daily (or on demand from the Actions tab) and installs a matching pg_dump for you. Add these repo secrets: BACKUPDRILL_DATABASE_URL, BACKUPDRILL_S3_BUCKET, BACKUPDRILL_S3_ACCESS_KEY_ID, and BACKUPDRILL_S3_SECRET_ACCESS_KEY; the endpoint, region, project name, and Storage-file variables are optional. Read the egress section above before going daily.

The hosted service runs this same engine — with scheduling, restore drills, failure alerts, and reports, so nothing here depends on you remembering to run it. Open the console or start with the quickstart.