Back up Supabase to AWS S3

S3 is the default answer to “where do backups go” — every backup tool speaks its API, and if your company already runs on AWS, the bucket lands next to your existing IAM, billing, and audit trail. This guide is the complete S3 setup for a Supabase project: bucket and least-privilege IAM user with the exact policy, CLI config (region instead of endpoint — simpler than R2 or B2), a weekly GitHub Actions schedule, and the lifecycle rules that keep a growing pile of snapshots cheap.

Why S3 — and what it costs

Numbers below are us-east-1, checked against AWS’s official pricing data (July 2026); other regions differ slightly.

  • Storage at $0.023/GB-month (S3 Standard, first 50 TB). A 10 GB project keeping ten snapshots — 100 GB — costs about $2.30/month. Request fees ($0.005 per 1,000 writes) round to zero at backup scale.
  • Egress at $0.09/GB — the number to know before you need it. Data transferred out to the internet is free for the first 100 GB per month aggregated across your AWS account, then $0.09/GB (first 10 TB tier). Backup writes into S3 are free on the AWS side; what meters is reading a snapshot back out — a real restore to a machine outside AWS, or a restore drill running outside AWS. Budget it as part of the recovery plan, not a surprise on the invoice.
  • The deepest archive ladder of the destinations in these guides. Lifecycle rules can walk old snapshots down the storage classes — Standard-IA at $0.0125/GB-month, Glacier Flexible Retrieval at $0.0036/GB-month for long-term retention — with the trade-offs covered in the lifecycle section below.

The honest counterweight: if you are not already on AWS, both of those first two numbers lose to Cloudflare R2 — $0.015/GB-month storage and zero egress — and the R2 version of this guide covers that setup, and Backblaze B2 undercuts both on storage price. And either way, the backup also pulls data out of Supabase, which counts against your Supabase plan’s egress allowance — the GitHub Actions guide walks through that math.

Step 1 — create the bucket

S3 console → Create bucket. Keep Block Public Access on— it is the default, and a backup bucket has no business being public. Pick the region deliberately: cross-region transfer is billed, so the cheapest restores and drills happen in the bucket’s own region. If your recovery plan is “spin up a Postgres on EC2”, put the bucket where that EC2 will run.

Step 2 — create a least-privilege IAM user

IAM → Users → create a dedicated user with no console access — it exists only to hold one access key for one bucket. Attach this inline policy — object actions on bucket/*, s3:ListBucket on the bucket itself, the same split AWS’s own policy examples use — swapping my-backups for your bucket name:

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:AbortMultipartUpload"],
      "Resource": "arn:aws:s3:::my-backups/*" },
    { "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::my-backups" }
  ]
}

That is everything the backup engine needs — write objects, read them back (restores and drills), clean up aborted multipart uploads, and list the bucket to find snapshots. No s3:DeleteObject: this key cannot destroy existing backups, so a leaked CI secret cannot take your history with it. Deletion stays with the lifecycle rules below, which run under the bucket’s own configuration rather than this credential.

Then, on that user: Security credentials Create access key → copy the Access Key ID and Secret Access Key.

Step 3 — region instead of endpoint

Unlike R2 or B2, AWS S3 needs no custom endpoint — S3 is what every S3-compatible tool defaults to, and the SDK derives the regional endpoint from the region name. Three values:

Endpoint: (leave blank — AWS S3 is the default)
Region:   your bucket's region, e.g. us-east-1
Path-style: not needed (AWS uses virtual-hosted addressing)

Step 4 — point the CLI at S3 and run the first backup

The open-source backupdrill CLI (MIT, github.com/backupdrill/cli) dumps the database with pg_dump, optionally copies your Storage files, and writes a checksummed manifest.json next to the dump. Configure it with env vars:

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_REGION="us-east-1"   # your bucket's region
export BACKUPDRILL_S3_BUCKET="my-backups"
export BACKUPDRILL_S3_ACCESS_KEY_ID="…"
export BACKUPDRILL_S3_SECRET_ACCESS_KEY="…"
# no BACKUPDRILL_S3_ENDPOINT — AWS S3 is the default

npx backupdrill backup

The connection string is the Session Pooler URI from the Connect panel (the Connect button at the top of the dashboard) — the default postgres user in it works as-is. Or keep the non-secret parts in a config file (backupdrill.config.json, gitignored in the CLI repo’s template) and the secrets in env vars:

{
  "databaseUrl": "postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres",
  "projectName": "my-app",
  "schemas": ["public"],
  "storage": {
    "region": "us-east-1",
    "bucket": "my-backups",
    "accessKeyId": "PUT-IN-ENV-OR-HERE",
    "secretAccessKey": "PUT-IN-ENV-OR-HERE",
    "prefix": "backupdrill"
  }
}
npx backupdrill backup --config backupdrill.config.json

To capture Supabase Storage files in the same snapshot — which you want, because a database restore alone does not bring files back — also set the four BACKUPDRILL_SUPABASE_STORAGE_* variables with keys from Dashboard → Storage → Settings → S3 Access Keys. When the run finishes, the bucket holds a timestamped snapshot: dump.pgcustom, manifest.json, and a storage/ tree if Storage is configured.

Step 5 — schedule it with GitHub Actions

A backup you run by hand is a backup that stops the week you get busy. The GitHub Actions guide has the complete workflow file and the Supabase egress arithmetic; the S3 specifics are just the repository secrets. Required: BACKUPDRILL_DATABASE_URL, BACKUPDRILL_S3_BUCKET, BACKUPDRILL_S3_ACCESS_KEY_ID, BACKUPDRILL_S3_SECRET_ACCESS_KEY. For AWS S3, set BACKUPDRILL_S3_REGIONto the bucket’s region and leave BACKUPDRILL_S3_ENDPOINT unset.

Lifecycle rules: keep old snapshots without paying Standard rates

S3’s storage classes are its real edge as a backup destination, and lifecycle rules move snapshots between them automatically — bucket → Management → Create lifecycle rule, scoped to the backupdrill/prefix (the CLI’s default key prefix). A sensible ladder for backup data:

  • Days 0–30: S3 Standard. These are the snapshots a drill reads and a real recovery would use — instant access, no retrieval fees.
  • Day 30: transition to Standard-IA at $0.0125/GB-month — about half of Standard. The catches: Standard-IA bills a $0.01/GB retrieval fee when data is read back and carries a 30-day minimum storage duration, so it fits snapshots you keep for history but rarely touch. Day 30 is also the floor: S3 lifecycle rules do not allow transitioning an object into Standard-IA before it is 30 days old.
  • Long tail: Glacier classes, with eyes open. Glacier Flexible Retrieval is $0.0036/GB-month with a 90-day minimum duration (Deep Archive: 180 days) — but Glacier objects must go through a restore-from-archive step before they can be downloaded at all. No tool that reads snapshots directly — a drill included — can use them in place. Glacier is for the compliance tail you will probably never read, not for operational backups.
  • Expiration. The same rule can delete snapshots past your retention window — and since the IAM policy above deliberately lacks s3:DeleteObject, lifecycle expiration is how old snapshots get cleaned up.

Prove it restores

A snapshot in S3 is a file, not a recovery plan — the only way to know it restores is to restore it. Run backupdrill drill to pull the latest snapshot into a throwaway Docker Postgres and verify it against the manifest, or walk through the full restore-testing guide — and remember that a drill run outside AWS reads the snapshot out at $0.09/GB once past the free 100 GB/month.

FAQ

How much does it cost to store Supabase backups in S3?

Storage is the small part: S3 Standard in us-east-1 is $0.023/GB-month, so a 10 GB project keeping ten snapshots — 100 GB — is about $2.30/month, and a lifecycle rule that moves older snapshots to Standard-IA ($0.0125/GB-month) roughly halves their share. The costs people miss are on the wire: reading a snapshot back out to the internet — a restore to your machine, or a restore drill running outside AWS — is billed at $0.09/GB after the first 100 GB/month of free data transfer aggregated across your AWS account, and each backup also counts as egress on the Supabase side.

Should I use S3 Glacier for Supabase backups?

Only for old snapshots you keep for compliance, never for the ones you restore-test. Glacier Flexible Retrieval costs $0.0036/GB-month (us-east-1) — roughly a sixth of Standard — but objects carry a 90-day minimum storage duration (180 days for Deep Archive), and reading them requires a restore-from-archive step before the object is downloadable at all, which breaks any tool that expects to read the snapshot directly, including a drill. A sane split: recent snapshots in Standard, a 30-day lifecycle transition to Standard-IA, and Glacier only for a long compliance tail.

Should I use AWS S3 or Cloudflare R2 for Supabase backups?

If your infrastructure already lives on AWS — IAM in place, one bill, backups in the same account as everything else — S3 is the path of least resistance, and lifecycle transitions into the Glacier classes give it the deepest archive ladder of the destinations covered in these guides. If you are choosing fresh, R2 undercuts it on both axes that matter for backups: storage is $0.015/GB-month against S3 Standard's $0.023, and egress is free against S3's $0.09/GB after the free allowance — which is exactly the meter a restore or a restore drill runs on. Either way the backup itself is identical: same CLI, same dump.pgcustom, same manifest.

Sources

Facts and prices last verified July 11, 2026 against the sources above. Written by the team behind BackupDrill.

Prefer the schedule, the restore drills, and the reports without owning the workflow? BackupDrill runs this same engine against your S3 bucket on a schedule — open the console (the Free plan covers weekly backups for one project), see the bucket setup docs for the shorter click-path version, or keep it DIY with the open-source CLI.