Self-hosted Supabase Storage on S3-compatible object storage

How to point self-hosted Supabase Storage at MinIO or another S3-compatible service instead of the local disk.

Self-hosted Supabase stores uploaded files on the local filesystem by default. That is fine for testing, but it ties your files to one server’s disk, complicates backups and fills the root volume. The Storage service can use any S3-compatible backend instead.

Why move off the file backend

  • Files survive a rebuild of the Supabase host
  • Object storage scales independently of server disk
  • Lifecycle rules and versioning are available
  • Backups become a bucket replication job rather than a filesystem copy

Pick a backend

Any S3-compatible service works: MinIO on the same or another server, or a hosted provider such as Cloudflare R2, Backblaze B2, Wasabi or a VPS provider’s object storage. For a single server setup, MinIO next to Supabase is common, but storing files on a separate provider gives you real offsite durability.

Configure the Storage container

In the self-hosted Docker setup, the Storage service reads its backend from environment variables. Change these in the Compose file or .env:

STORAGE_BACKEND=s3
GLOBAL_S3_BUCKET=supabase-storage
GLOBAL_S3_ENDPOINT=https://s3.example.com
GLOBAL_S3_PROTOCOL=https
GLOBAL_S3_FORCE_PATH_STYLE=true
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
REGION=us-east-1

FORCE_PATH_STYLE is required for MinIO and many other S3-compatible services. Create the bucket before starting Storage. Names of variables have changed across releases, so compare with the docker-compose.s3.yml example in the Supabase repository for your version.

Moving existing files

Files already uploaded live under the storage volume, organized by tenant, bucket and object path. Copy them into the new bucket with the same layout using rclone or mc mirror, then restart Storage with the new backend. Spot check downloads of old files before deleting the local copies.

Image transformations

If you use image resizing, the imgproxy container needs access to files too. With an S3 backend, Storage fetches the object and passes it to imgproxy, so no extra mount is needed, but check that transformations still work after the switch.

Permissions stay in Postgres

Row level security policies on storage.objects decide who can read and write. The backend change does not affect them. Keep the bucket itself private and give Supabase the only credentials.

Backups

Enable versioning on the bucket or replicate it to a second provider. Remember that the file metadata lives in Postgres, so a database backup and a bucket backup taken at different times can disagree. Back up both on the same schedule.

For the full stack, from secrets to which services you can disable, see this guide to self-hosting Supabase on a VPS.

Disclosure: I help build Peon.


Write a comment