file storage · side by side
Supabase Storage vs Vercel Blob for a Next.js app
Both fill the file storage slot, so a generated repo carries one or the other, never both. Every line below is read out of the two manifests.
Short answer
Pick Supabase Storage if
Teams already on Supabase who want files under the same policies as their rows. Same migrations, same pull request. Strongest for private per-user files (avatars, invoices, imports) where "the owner and nobody else" is the whole access model.
Pick Vercel Blob if
Apps already on Vercel that want user uploads (avatars, attachments, imports) without adding a cloud account, IAM, CORS rules or a second bill. Strongest when files are small to medium and read by their owner.
Vercel Blob is tested with Neon. Supabase Storage isn't yet.
Side by side
Price, obligations, and the surface each one adds. No row is written by hand. This is manifest.yaml, rendered.
| From the manifest | Option ASupabase Storage | Option BVercel Blob |
|---|---|---|
| In one line | Supabase Storage Object storage in your Supabase project, with the same row level security as tables. | Vercel Blob File storage inside your Vercel project. One env var to wire, no second vendor. |
| Pricing | Supabase Storage Part of your Supabase plan. Free: 1 GB stored and 5 GB egress. Pro: 100 GB stored, then $0.0213/GB, and 250 GB egress, then $0.09/GB ($0.03/GB for cached egress). Image transformations need Pro: 100 origin images included, then $5 per 1,000. | Vercel Blob Hobby: free up to 1 GB stored, 10,000 simple and 2,000 advanced operations, and 10 GB of transfer a month. Past the limit, Blob stops until the 30 days reset. Pro bills by usage. Prices below are for iad1. Storage is $0.023 per GB-month and transfer $0.05 per GB. Simple operations (cache-miss reads) are $0.40 per million. Advanced operations (put, copy, list) are $5 per million. Deletes are free. Client uploads have no transfer charge. |
| Best for | Supabase Storage Teams already on Supabase who want files under the same policies as their rows. Same migrations, same pull request. Strongest for private per-user files (avatars, invoices, imports) where "the owner and nobody else" is the whole access model. | Vercel Blob Apps already on Vercel that want user uploads (avatars, attachments, imports) without adding a cloud account, IAM, CORS rules or a second bill. Strongest when files are small to medium and read by their owner. |
| Trade-offsVerbatim from the manifest | Supabase Storage
| Vercel Blob
|
| Required companionsAdded for you, with a reason | Supabase Storage
| Vercel Blob Nothing. It stands on its own. |
| Recommended alongsideSuggested, never added for you | Supabase Storage
| Vercel Blob
|
| Env vars you will manageEvery one documented in docs/onboard.md | Supabase Storage 3 variables · 1 required
| Vercel Blob 4 variables · 1 required
|
| Dependencies added | Supabase Storage
| Vercel Blob
|
| MCP serversWritten into .mcp.json | Supabase Storage None. No extra agent tools from this one. | Vercel Blob None. No extra agent tools from this one. |
| Footprint in your repo | Supabase Storage 12 files, plus 3 injections into shared stack files | Vercel Blob 13 files, plus 3 injections into shared stack files |
What changes in your repo
The paths each battery contributes, diffed. A path in the third list is written by both, so swapping rewrites that file rather than adding one.
Only with Supabase Storage (2)
supabase/1 file
migrations/1 file
- 20250101000200_storage_bucket.sql
variants/1 file
auth-supabase/1 file
supabase/1 file
migrations/1 file
- 20250101000201_storage_policies.sql
Only with Vercel Blob (3)
src/2 files
lib/2 files
storage/2 files
- access.ts
- confirm.ts
variants/1 file
auth-clerk/1 file
slots/1 file
- auth-public-routes.ts
Same path, different implementation (10)
src/5 files
app/1 file
api/1 file
upload/1 file
- route.ts
components/1 file
upload/1 file
- file-dropzone.tsx
lib/3 files
storage/3 files
- authorize.ts
- index.ts
- keys.ts
tests/1 file
unit/1 file
- storage-keys.test.ts
variants/4 files
auth-none/1 file
src/1 file
lib/1 file
storage/1 file
- uploader.ts
auth-wired/1 file
src/1 file
lib/1 file
storage/1 file
- uploader.ts
errors-none/1 file
src/1 file
lib/1 file
storage/1 file
- report.ts
errors-sentry/1 file
src/1 file
lib/1 file
storage/1 file
- report.ts
Shared stack files Supabase Storage injects into
- env-required
- legal-processors
- verify-checks
Shared stack files Vercel Blob injects into
- env-required
- legal-processors
- verify-checks
Supabase Storage in your .env.local
# required
SUPABASE_STORAGE_BUCKET=uploads
# optional
STORAGE_ALLOWED_ORIGINS=https://app.example.com,https://staging.example.com
STORAGE_DEV_UPLOADER=dev-user
Vercel Blob in your .env.local
# required
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_0123456789abcdef_0123456789abcdef0123456789abcdef
# optional
STORAGE_ALLOWED_ORIGINS=https://app.example.com,https://staging.example.com
STORAGE_DEV_UPLOADER=dev-user
VERCEL_BLOB_CALLBACK_URL=
What changes for your agents
Each battery ships rules, skills, subagents and hooks that an agent loads before it touches the code that battery owns. Picking one is also picking how your agents behave in src/lib/storage/**.
Supabase Storage
2
Skills
2
Rules
5
Solution docs
Rules (2)
Buckets and their policies are migrations, not console clicks
supabase/migrations/** · src/lib/storage/**
Never sign a URL without an authorisation check, and never proxy uploads
src/lib/storage/** · src/app/api/upload/** · src/components/upload/**
Skills (2)
/add-bucket-policy
Add or change a Supabase Storage bucket and its row level security policies as a migration, then prove the policy actually denies what it should.
/upload-flow
Add a complete file upload to a feature: authorised signing route, direct-to-storage upload, the database row that records the key, and the cleanup that stops orphans.
Subagents and hooks
None of its own. The foundation agents and guard hooks still ship.
Vercel Blob
2
Skills
2
Rules
7
Solution docs
Rules (2)
Vercel Blob store - access mode, credentials, cache, cost
src/lib/storage/** · scripts/verify.ts
Client uploads - auth before the token, the server names the key, the token is locked down
src/lib/storage/** · src/app/api/upload/** · src/components/upload/**
Skills (2)
/add-upload-kind
Add a new kind of upload (avatars, attachments, imports) to the Vercel Blob flow, with its own folder, type and size limits, the row that owns the key, and the cleanup that stops orphans.
/clean-orphaned-blobs
Find and delete Vercel Blob objects no database row points at, safely, in batches, without deleting uploads that are still in flight.
Subagents and hooks
None of its own. The foundation agents and guard hooks still ship.
What each one already knows
Solution docs land in docs/solutions/ in your repo and are published here, so you can read the failure modes before you commit.
Supabase Storage (5)
- Cleaning up orphaned uploads before they become the billEvery abandoned upload and every deleted row leaves an object nothing references. Here is where orphans come from, the sweeper that finds them, and the two-phase delete that stops making more.docs/solutions/supabase-storage/cleaning-up-orphaned-uploads.md
- Serving images from Supabase Storage without shipping 4 MB avatarsOn-the-fly transformation resizes at read time, but it is metered and it fights your cache. When to transform, when to resize on upload, and how signed URLs complicate both.docs/solutions/supabase-storage/image-transformation.md
- Public bucket or private bucket: decide once, per bucket, on purposeA public bucket serves every object to anyone who guesses a key, forever. A private one costs you a signing step and a cache problem. Here is how to choose, and why mixing them in one bucket goes wrong.docs/solutions/supabase-storage/public-vs-private-buckets.md
- Row level security on storage buckets, and why yours might not be runningStorage policies are SQL against storage.objects keyed on the path. Here is the policy set that works, the key layout it depends on, and why the service role key silently bypasses all of it.docs/solutions/supabase-storage/rls-on-storage-buckets.md
- Signed upload URLs versus proxying the file through your serverProxying uploads through a route handler hits body limits, doubles the transfer and bills you for the wait. Sign a URL instead, and get the order of the checks right.docs/solutions/supabase-storage/signed-upload-urls-vs-proxying.md
Vercel Blob (7)
- Vercel Blob addRandomSuffix, allowOverwrite, and stale files in the cacheBlob refuses to overwrite by default, and an overwrite can take 60 seconds to show plus whatever the browser cached. Treat blobs as immutable; use new pathnames, not overwrites.docs/solutions/vercel-blob/addrandomsuffix-allowoverwrite-and-the-cache.md
- Vercel Blob: client uploads vs server uploads and the 4.5 MB limitA Vercel Function accepts at most 4.5 MB of request body. Server uploads hit it; client uploads skip it. How client uploads work, and when a server upload is still right.docs/solutions/vercel-blob/client-uploads-and-the-4-5-mb-body-limit.md
- Deleting orphaned blobs in Vercel BlobBlobs nobody references still bill every month. Where orphans come from, how to delete them in the same code path as the row, and a safe sweep with list() and del() for the rest.docs/solutions/vercel-blob/deleting-orphaned-blobs.md
- Vercel Blob handleUpload: never trust the pathname from the clientWith client uploads the browser names the pathname and the token is bound to it. Check it in onBeforeGenerateToken, or any signed-in user can write anywhere in your store.docs/solutions/vercel-blob/never-trust-the-client-pathname.md
- Why Vercel Blob onUploadCompleted never fires on localhostonUploadCompleted is a webhook from Vercel to your app. It cannot reach localhost, so the SDK skips it. Use a tunnel and VERCEL_BLOB_CALLBACK_URL, and never make the upload depend on it.docs/solutions/vercel-blob/onuploadcompleted-never-fires-on-localhost.md
- Private vs public Vercel Blob stores: picking one you cannot changeA Blob store's access mode is fixed at creation. Private needs a credential for every read; public is readable by anyone with the URL. How to serve each, and why user files belong in private.docs/solutions/vercel-blob/private-vs-public-blob-stores.md
- What Vercel Blob actually costs, and where egress hidesStorage is cheap. Advanced operations and data transfer are the lines that grow. How each is counted, why private delivery can cost more, and the habits that keep the bill flat.docs/solutions/vercel-blob/vercel-blob-costs-and-egress.md
Which one to pick
From meta.bestFor and meta.tradeoffs. If a claim is not in the manifest, it is not on this page.
Pick Supabase Storage when
Teams already on Supabase who want files under the same policies as their rows. Same migrations, same pull request. Strongest for private per-user files (avatars, invoices, imports) where "the owner and nobody else" is the whole access model.
And accept that(6)
- Egress is billed. If you serve large media at volume, the transfer line will outgrow the storage line. That is the case R2 exists for.
- Policies are SQL against
storage.objects, so the key layout is part of your security model. Change the shape of your keys and every policy changes with it. - The service role key bypasses every policy. Server code that uses it does its own authorisation, and the RLS policies only guard what holds a user token.
- Image transformation is convenient and metered per origin image. Cheap for avatars, surprising for a gallery.
- Signed upload URLs last two hours and that cannot be shortened, so treat the URL itself as a credential.
- One bucket per access model, not per feature. Public and private objects in one bucket end in leaked files or a pile of policy exceptions.
Pick Vercel Blob when
Apps already on Vercel that want user uploads (avatars, attachments, imports) without adding a cloud account, IAM, CORS rules or a second bill. Strongest when files are small to medium and read by their owner.
And accept that(6)
- Egress is billed. Blob data transfer is cheaper than Vercel's regular CDN transfer, but it is not zero. Large media served at volume is the case R2 exists for.
- A store is private or public forever. You pick at creation and cannot change it. Mixing both means two stores.
- Advanced operations are the expensive line. Every put, copy and list counts, and so does browsing the store in the Vercel dashboard.
- Authorisation is your code. Blob has no row level security. The upload route decides who may write and where, every time.
- onUploadCompleted is a webhook from Vercel to your app. It cannot reach localhost, so local testing needs a tunnel.
- Overwrites and deletes take up to 60 seconds to leave the cache. Treat blobs as immutable and give every version a new pathname.
Questions people actually ask
- Should I choose Supabase Storage or Vercel Blob?
- Supabase Storage is best for teams already on Supabase who want files under the same policies as their rows. Same migrations, same pull request. Strongest for private per-user files (avatars, invoices, imports) where "the owner and nobody else" is the whole access model. Vercel Blob is best for apps already on Vercel that want user uploads (avatars, attachments, imports) without adding a cloud account, IAM, CORS rules or a second bill. Strongest when files are small to medium and read by their owner. Both fill the file storage slot, so a generated repo carries one or the other, never both.
- How much do Supabase Storage and Vercel Blob cost?
- Supabase Storage: Part of your Supabase plan. Free: 1 GB stored and 5 GB egress. Pro: 100 GB stored, then $0.0213/GB, and 250 GB egress, then $0.09/GB ($0.03/GB for cached egress). Image transformations need Pro: 100 origin images included, then $5 per 1,000. Vercel Blob: Hobby: free up to 1 GB stored, 10,000 simple and 2,000 advanced operations, and 10 GB of transfer a month. Past the limit, Blob stops until the 30 days reset. Pro bills by usage. Prices below are for iad1. Storage is $0.023 per GB-month and transfer $0.05 per GB. Simple operations (cache-miss reads) are $0.40 per million. Advanced operations (put, copy, list) are $5 per million. Deletes are free. Client uploads have no transfer charge.
- What changes in my repo if I switch from Supabase Storage to Vercel Blob?
- Supabase Storage writes 12 files, 3 environment variables and 2 dependencies, and installs 2 path-scoped rules, 2 skills and 5 solution docs. Vercel Blob writes 13 files, 4 environment variables and 2 dependencies, and installs 2 path-scoped rules, 2 skills and 7 solution docs.
Decide once, then build the repo that already knows the decision.
Either way you get that choice’s rules, skills and solution docs installed, plus the guard hooks, an onboarding doc for exactly these env vars, and the Compound Engineering loop. Free and MIT.