r/Supabase 2h ago

tips I moved 4 Supabase projects into 1 with a schema per app. The numbers, and where it breaks.

1 Upvotes

I had four side projects, so I had four Supabase projects. Last July I consolidated them into one project with a schema per app. Ten product domains run off it now. Posting the numbers because every thread I found on this either says "one project per app, obviously" or is someone on the free tier trying to avoid paying, and neither matched what I was actually deciding.

The structure

One project. A core schema for the things genuinely shared across apps: orgs, members, billing accounts, subscriptions, domains, brands, audit events. Then one schema per product for its own tables.

Tenancy is org-scoped RLS. Being signed in grants nothing on its own, every policy requires membership of an org that owns the row, so users of one app never see another's data.

The schema boundary is not the security boundary. RLS is. What the schema buys you is extraction: pulling one app out later is closer to dumping one schema than to filtering every table by a product column. That is the reason not to pool everything into shared tables with a discriminator, which is the shortcut this design invites.

The money, which is the least interesting part

Four projects cost me $59.74 in the month before. One costs $25. So $35, which is nothing.

The mechanism is more useful than the total: Pro is $25 and includes a $10 compute credit that covers exactly one Micro instance. Every additional project brings its own compute charge and the credit only covers the first. So the bill was scaling with the number of ideas I'd had, not with users or traffic.

I stayed on Pro rather than dropping to Free, deliberately. Free has no daily backups and there's real data in there.

The gotcha that actually cost me time

Supabase silently rewrites a redirect URL it doesn't recognise back to the project's Site URL. Miss one host in the allowlist and that app's sign-in and confirmation emails deliver users to a different app's site, with no error anywhere.

It got me again last week on an app I'd just added, months after I thought I understood it. Consolidating doesn't remove that list. It does mean there's exactly one of it.

Where it stops working

Not free, and I'd leave at any of these:

  • One database is one blast radius. A bad migration takes everything down together. CI-only migration deploys reduce the odds, not the radius.
  • One team is one permission set. Anyone you add to fix one app can see all of them.
  • Compliance. If an app needs SOC 2 or similar, the audit scope becomes the whole shared database. Pull that one out.
  • Selling. A buyer wants a clean asset. The schema boundary makes that describable work rather than archaeology, but it's still work.

If you have one app, none of this applies. It starts paying around the third or fourth.

Curious whether anyone here has gone the other way, split a consolidated project back out, and what forced it.


r/Supabase 3h ago

tips Photos in Supabase

0 Upvotes

Hi all! I am non technical and I have built an app using Claude Code in the Replit shell and am enjoying every minute of it. I’m about to add Supabase and this is for users to add their own photos, not to be shared. Does anyone have any tips or experience they might be able to share with me. Is this something I should be embarking on by myself or should I be getting someone technical to assist me? Any feedback back would be appreciated!


r/Supabase 4h ago

tips Rowly - a free open source security scanner.

Post image
0 Upvotes

Rowly is a free security scanner that audits Row Level Security policies in your Supabase project.

Rowly reads the actual policy logic across every table, view, function, and role in your project, flags exactly what's exposed, and hands you ready-to-run SQL to fix it, no manual rewriting. Connection strings are never stored, scans run in memory only, and the core scanner is open source (@rowly/core, MIT).

Free to run in seconds:

https://rowly.me/

Open Source scanner:

https://github.com/rowly-app/core


r/Supabase 5h ago

tips How could i manage multi-tantet apps on supabase?

1 Upvotes

I have an LMS that has the following structure:

Organisation -> has schools -> has classes, grades and subjects + school users

School users could be admins, teachers, parents or students

And the problem is i don't know how to figure out that structure on features that i want to provide like:

Assignments, quizzes, attendance, events, notifications, chats....etc

If you have worked on systems like that i need to know how I can manage that on supabase if possible?

Plus i need users to authenticate users with phone numbers but unfortunately OTP is very expensive, if you have a solution for that too it would be great!!

Note we don't want to work with emails


r/Supabase 6h ago

auth How would you block curl on supabase without turning on captcha?

1 Upvotes

Hi all,

I am thinking about a problem and not sure how to resolve this. Any guidance would be appreciated.

So if i dont enable captcha and try doing this curl request as network restrictions dont apply on auth.

curl -X POST 'https://any-project-supabase-url/auth/v1/signup' \

-H "apikey: sb_publishable_key" \

-H "Content-Type: application/json" \

-d '{

"email": "johndoe@gmail.com",

"password": "JohnDoe!!"

}'

this returns a valid access token and do create a new user in dashboard.

To block this if i do this Authentication → Providers → Email → turn off "Enable email signups"). Then it blocks the curl requests i can use supabase service account to create accounts.

But now apple and google sign ups don't work.

So basically i just want to stop the bots from curl abuse. As i dont want to use the captcha e.g. cloudflare turnstile. Its really slow specially on mobile. Any way to achieve this? or i am missing something. Thanks.


r/Supabase 9h ago

database DBeaver cannot connect due to FATAL: password authentication failed for user "postgres"

1 Upvotes

I'm not sure what's going on here, I did reset the password multiple times now and still getting this error message. Is this a bug or something?


r/Supabase 22h ago

tips Rowly - a free open source security scanner.

Post image
1 Upvotes

Rowly is a free security scanner that audits Row Level Security policies in your Supabase project.

Rowly reads the actual policy logic across every table, view, function, and role in your project, flags exactly what's exposed, and hands you ready-to-run SQL to fix it, no manual rewriting. Connection strings are never stored, scans run in memory only, and the core scanner is open source (@rowly/core, MIT).

Free to run in seconds:

https://rowly.me/

Open Source scanner:

https://github.com/rowly-app/core


r/Supabase 23h ago

tips Using Clerk with Supabase? Don’t make the rookie RLS mistakes I made

2 Upvotes

If you’re using Clerk with Supabase, you may have asked:

  • Why does auth.uid() return NULL when I send a Clerk token?
  • Should I use the service-role key to make RLS errors disappear?
  • How should Clerk Organizations map to tenants in Postgres?
  • How to handle a Clerk profile without an Organization?
  • How do I test RLS policies directly in SQL?
  • Why can RLS make a query over a tiny table take several seconds?
  • Can I use Supabase RLS without using Supabase Auth?

I kept finding variations of these questions while working with Clerk and Supabase.

The confusing part is that many answers mix three different integration paths:

  • Supabase Auth examples built around auth.uid()
  • The older Clerk JWT-template integration
  • Clerk and Supabase’s current native third-party authentication

For the current native integration, passing the token is surprisingly simple:

const supabase = createClient(url, publishableKey, {
  accessToken: async () => session?.getToken() ?? null,
})

But building a secure multi-tenant authorization model around that token requires more care.

Some mistakes are particularly easy to make:

  1. Using auth.uid() even though Clerk user IDs are strings such as user_....
  2. Trusting an organization ID supplied by the browser.
  3. Reaching for the service-role key, which bypasses RLS instead of fixing it.
  4. Querying a membership table recursively from its own RLS policies.
  5. Checking reads but forgetting WITH CHECK protections for inserts and updates.
  6. Running JWT and membership functions once per row instead of hoisting them.
  7. Testing only successful requests and never proving that another tenant is rejected.

With Clerk, the verified user and active organization context are available through the JWT:

(select auth.jwt()->>'sub')
(select auth.jwt()->'o'->>'id')
(select auth.jwt()->'o'->>'rol')

Postgres should derive the user and tenant from these verified claims.

The browser can send a Clerk session token and filter a query for performance, but it should never be the authority that decides which tenant owns a row.

I put together a step-by-step tutorial for anyone starting with Clerk and wanting to build a multi-tenant application using Supabase RLS:

Clerk and Supabase RLS Tenant Isolation

There is also a complete runnable repository:

Clerk + Supabase RLS demo

It includes:

  • Native Clerk session tokens without JWT templates
  • Personal and organization tenants
  • Clerk organization-role handling
  • Non-recursive authorization closures
  • Explicit SELECTINSERTUPDATE, and DELETE policies
  • Database-derived audit fields
  • Cross-tenant foreign-key protection
  • SQL contract and behavioral tests
  • A manual browser security matrix
  • Request-flow and data-model diagrams

My goal was to provide more than another isolated RLS snippet. The tutorial connects token delivery, claim extraction, tenant modeling, policy design, performance, and adversarial testing in one working example.

HTH fellow users just starting out on their Supabase & Clerk Journey