1. Resources
  2. /
  3. Plugins
  4. /
  5. supabase-backup-buildkite-plugin

Supabase Backup Buildkite Plugin

Backs up a Supabase (or any Postgres) database to a bucket you own: AWS S3, Cloudflare R2, Backblaze B2, MinIO or anything else that speaks the S3 API. With drill: true it then restores the new backup into a throwaway Postgres container on the agent and checks that it came back.

The step runs the backupdrill CLI, an open-source (MIT) Supabase backup tool. The dump goes from your database through the agent straight to your bucket. Next to it the CLI writes a manifest.json (tables, estimated row counts, sizes, SHA-256 checksums) and a RECOVERY.md with restore steps.

Example

Back up before a deploy runs its migrations. If the backup fails, the steps after the wait don’t run.

steps:
  - label: ":floppy_disk: Back up Supabase"
    secrets:
      - BACKUPDRILL_DATABASE_URL
      - BACKUPDRILL_S3_ACCESS_KEY_ID
      - BACKUPDRILL_S3_SECRET_ACCESS_KEY
    plugins:
      - backupdrill/supabase-backup#v1.0.0:
          s3-endpoint: https://<account-id>.r2.cloudflarestorage.com
          s3-bucket: my-db-backups
          project-name: my-app

  - wait

  - label: ":rocket: Migrate"
    command: ./migrate.sh

Weekly backup with a restore drill

Create a scheduled build for a pipeline with this step, for example 0 3 * * 0 for Sundays at 03:00 UTC:

steps:
  - label: ":floppy_disk: Weekly Supabase backup and restore drill"
    secrets:
      - BACKUPDRILL_DATABASE_URL
      - BACKUPDRILL_S3_ACCESS_KEY_ID
      - BACKUPDRILL_S3_SECRET_ACCESS_KEY
    plugins:
      - backupdrill/supabase-backup#v1.0.0:
          s3-region: us-east-1
          s3-bucket: my-db-backups
          drill: true

The drill downloads the dump, checks its checksum, restores it into a postgres container with pg_restore, and compares the restored tables with the manifest: every table is there, and tables that had rows still have rows. The step fails if any check fails.

Secrets

Credentials never go in the plugin options, because those end up in the pipeline YAML. The plugin reads them from the job environment. The examples above use Buildkite secrets with the same names; an agent environment hook or your own secrets plugin works just as well.

VariableRequiredWhat it is
BACKUPDRILL_DATABASE_URLyesConnection string of the database, password included. For Supabase, open the project, click Connect and copy the Session pooler string. The default postgres user works.
BACKUPDRILL_S3_ACCESS_KEY_IDyesAccess key for the backup bucket.
BACKUPDRILL_S3_SECRET_ACCESS_KEYyesSecret key for the backup bucket.
BACKUPDRILL_SUPABASE_STORAGE_ENDPOINT, _REGION, _ACCESS_KEY_ID, _SECRET_ACCESS_KEYnoSet all four to copy Supabase Storage files as well. Create the keys under Storage > Settings > S3 Access Keys.
BACKUPDRILL_PING_URLnoA Healthchecks.io ping URL. The backup and the drill report start, success and failure to it.

Every other BACKUPDRILL_* variable the CLI knows works too; the CLI docs list them. DATABASE_URL is ignored on purpose: in CI it usually points at the app’s test database, and the CLI would otherwise fall back to it. A backupdrill.config.json in the checked-out repository is ignored for the same reason.

The drill only reads the bucket. It runs without the database URL and the Storage keys.

Options

All options are optional and safe to commit. An option wins over the matching BACKUPDRILL_* variable.

OptionDefaultDescription
s3-bucketBACKUPDRILL_S3_BUCKETBucket the backup is written to. Required one way or the other.
s3-endpointnoneEndpoint of an S3-compatible service (R2, B2, MinIO). Leave it out for AWS S3.
s3-regionautoBucket region. AWS S3 needs the real one.
s3-prefixbackupdrillKey prefix inside the bucket.
project-namesupabaseName used in the object keys and the manifest.
schemaspublicSchemas to dump, as a list or a comma-separated string. An empty entry in the list is an error.
drillfalseRestore the new backup into a throwaway Postgres and check it.
meta-data-keybackupdrill-snapshotBuild meta-data key the snapshot name is stored under. Give each step its own key when one build backs up several projects.
backupdrill-version2CLI version from npm: 2 means the newest 2.x release, 2.8.1 pins one.

Agent requirements

  • Node.js 20 or newer, with node and npm on the PATH. Each run installs the CLI from the public npm registry (https://registry.npmjs.org/) into a temporary directory, without install scripts. npm settings from the repository being built or from the agent user’s .npmrc don’t apply to that install, so a repository can’t swap in another registry.
  • pg_dump with a major version at least as new as the server. Supabase runs Postgres 15 to 17, so pg_dump 17 covers every project. The CLI checks the version and refuses a mismatch. To use a binary that isn’t first on the PATH, set BACKUPDRILL_PG_DUMP.
  • For drill: true: Docker, and pg_restore from the same Postgres version as pg_dump (BACKUPDRILL_PG_RESTORE to point at it). The drill publishes the sandbox port on 127.0.0.1, so the job has to run where that address reaches the Docker host. A job running directly on the agent host does; one inside a container needs host networking and the Docker socket.

What the step leaves behind

  • An annotation with the result, for example: Backed up 2 tables (12.8 KiB dump) to snapshot 2026-10-31T08-17-48-954Z with backupdrill 2.8.1. Restore drill passed: 2 tables and 1,300 rows restored in 0.1s.
  • The snapshot name in the build meta-data under backupdrill-snapshot (or your meta-data-key), for later steps: buildkite-agent meta-data get backupdrill-snapshot.
  • The backup in your bucket:
<s3-prefix>/<project-name>/<snapshot>/
  dump.pgcustom            pg_dump custom format
  storage/<bucket>/<key>   only with the Storage keys set
  RECOVERY.md
  manifest.json            written last, so a snapshot without it is incomplete

The manifest lists every table name and Storage file path. The CLI prints it on standard output, and the plugin keeps that out of the build log.

To get the data back, backupdrill restore restores a snapshot into a new Supabase project (database and Storage files), or you can load dump.pgcustom with pg_restore. RECOVERY.md in each snapshot has the commands.

Cost

Each backup dumps the schemas you chose in full, and that traffic counts as egress on a Supabase bill. Read the egress notes in the CLI docs before you schedule it daily.

Developing

docker compose run --rm tests   # bats tests, with npm, the CLI and buildkite-agent stubbed
docker compose run --rm lint    # Buildkite plugin linter

tests/e2e/run.sh runs the hook for real: it starts Postgres 17 and a SeaweedFS S3 store in Docker, loads a small test database, backs it up and drills the snapshot. It needs the same tools as an agent (see above), or run it in the image under tests/e2e:

docker build -t supabase-backup-e2e tests/e2e
docker run --rm --network host -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$PWD:/plugin:ro" -w /plugin supabase-backup-e2e bash tests/e2e/run.sh

License

MIT

The plugins listed on this webpage are provided for informational purposes only. They have not undergone any formal security review or assessment. While we strive to provide useful resources, we cannot guarantee the safety, reliability, or integrity of these plugins. Users are strongly advised to conduct their own security evaluations before downloading, installing, or using any plugin. By using these plugins, you acknowledge and accept any risks associated with their use. We disclaim any liability for any harm or damages arising from the use of the plugins listed.

You deserve better CI.

Get started for free with a full month of our all-access trial.

Buildkite Pipelines

Platform

  1. Pipelines
  2. Public pipelines
  3. Test Engine
  4. Package Registries
  5. Pricing

Hosting options

  1. Self-hosted agents
  2. Mac hosted agents
  3. Linux hosted agents
  4. Windows hosted agents

Resources

  1. Docs
  2. Blog
  3. Changelog
  4. Example pipelines
  5. Plugins
  6. Webinars
  7. Case studies
  8. Events
  9. Migration Services

Company

  1. About
  2. Careers
  3. Press
  4. Security
  5. Brand assets
  6. Contact

Solutions

  1. Replace Jenkins
  2. Workflows for MLOps
  3. Testing at scale
  4. Monorepo mojo
  5. Bazel orchestration

Compare

  1. All comparisons
  2. Buildkite vs GitHub Actions
  3. Buildkite vs Jenkins
  4. Buildkite vs CircleCI
  5. Buildkite vs Harness
  6. Buildkite vs CloudBees
  7. Buildkite vs Blacksmith
  8. Buildkite vs Depot
  9. Buildkite vs Dagger

Legal

  1. Terms of Service
  2. Acceptable Use Policy
  3. Privacy Policy
  4. Subprocessors
  5. Service Level Agreement
  6. Supplier Code of Conduct
  7. Modern Slavery Statement

Support

  1. System status
  2. Forum
© Buildkite Pty Ltd 2026