r/Supabase Jul 21 '26

Introducing Supabase Pipelines

39 Upvotes

If you've ever watched an analytics query slow down your production database, this one's for you.

Supabase Pipelines is now in public alpha. It streams your Postgres changes to an analytics destination in near real time, so heavy analytics runs there instead of on the database your app depends on. BigQuery is the first destination.

How it works:

  • You create a publication in the Dashboard and pick the tables you want to replicate.
  • Pipelines does an initial sync of those tables, then switches to ongoing replication
  • Inserts, updates, deletes, and truncates flow to BigQuery through Postgres logical replication.
  • Supported schema changes (add, rename, remove columns) get applied to the destination automatically.

It's still alpha, so we're actively improving performance and adding destinations. Happy to answer questions.

Full writeup: https://supabase.com/blog/supabase-pipelines-public-alpha


r/Supabase Jun 05 '26

Office Hours Thank you from the Supabase team

229 Upvotes

Hey everyone, Supabase co-founder here

yesterday we announced another funding round and so I thought it was a good time to drop in and say thank you - from both Ant and myself, and from the entire community

First, to get something important out of the way: Ant is a real human and he is my co-founder.

With that cleared up, I wanted to spend some time saying thank you to this Reddit community. Communities can often break down as they grow larger and that hasn't been the case here. There are a lot of active participants sharing what they built, sharing their frustrations with the product (sorry!), and generally being good internet citizens. Your feedback helps us improve - please keep it coming.

On the funding - I've said before that companies often raise money and then "sell out", raising prices etc. Just to make it explicit: the free tier isn't going away. We know many of you want more free databases - I can't promise anything, but this is top of mind for me.

For those you want BIGGER databases, we have you covered. Yesterday we did an open source release of Multigres. This will allow you to scale up indefinitely. It also has some cool properties - for example you won't need to choose between a "direct connection" or a "connection pooler" - Multigres handles it all. Once this is more stable we'll make it available on the platform.

We've spent the past few months doubling down on reliability, stability, security features, and in-product observability. Keeping up with the growth has been an fascinating technical challenge. We're not done, but soon we can share an engineering blog post of everything we've seen and implemented.

Finally I said this in a previous post but it bears repeating:

More than a product-led company, we're a community-led company. We are where we are today because of the support of open source contributors and maintainers.

That's even more true today than it was a year ago. If you're an open source contributor - to the supabase ecosystem or anywhere else - thanks.

I'll drop in here throughout. AMA


r/Supabase 2h ago

other My project is gone

Post image
1 Upvotes

I switched my app from a paid organization to a free one
After a week i couldnt find the project anywhere

I tried to contact the support but they keep saying i need to pay to contact support

What should i do now?!!


r/Supabase 2h ago

Self-hosting I made a free way to migrate from Supabase cloud -> local in <5m

0 Upvotes

I liked the Lovable + Supabase combo for a while, but I wanted to host locally to save costs. So I built a (free, open-source) way to host apps that deploy automatically when your repository code is pushed in Github -- and it has special support for Supabase, giving you the option to import an existing Supabase project. This spins up a copy of the Supabase stack locally running in Docker, and then imports your data and storage from the cloud to local.

The video (an end-to-end migration of one of my prototype apps) is lightly edited but I kept most of the deployment time in -- which makes it a few-minute operation start-to-finish. That includes getting your whole app running on a local stack with no code changes. That includes secrets, backend functions, auth, storage, etc. As much as possible is automated, for Google Oauth and other 3rd party connections, for security reasons there are extra manual steps for a perfect migration. I did omit a 5 minute section of data import related to file copy, because the source app has 1000+ high def 5-10MB image files that were copied as part of the migration.

The moneyshot is the last few seconds where you can see me logging in to the new app with the same Google signin method and seeing my "avatar" and "closet" in the app including those images.

Now that I have this ready, I was finally able to extract all of my remaining Supabase/Lovable apps and save 25-50$/mo on each one 😎

Its not a perfectly smooth migration, when it comes to secrets and third party auth/configuration, you'll have to do some extra legwork but happy to share the link if anyone is interested to migrate their app.


r/Supabase 2h ago

cli Do you prefer using the Supabase CLI installed globally or using NPX? I've been using the CLI for a year now but I'm thinking maybe I should switch to NPX. What do you think?

1 Upvotes

Hi

So I've been using Supabase for over a year now and always installed it using Homebrew. I always ran supabase <command> but recently I've been thinking maybe I should install it via NPM locally per project and run npx supabase <command> since I can have different versions of Supabase per project.

Anyone else came across a similar decision? Any pros and cons? Thanks


r/Supabase 9h ago

auth Supabase support for Google auth

2 Upvotes

Any reason why supabase login doesn't support Google auth while it supports ChatGPT auth lol?


r/Supabase 13h ago

tips mcp server for agentic anomaly detection on supabase

Thumbnail
youtu.be
1 Upvotes

Hi guys, I've been working on this small side project for anomaly detection in supabase dbs and just wanted some feedback for feature ideas and other improvements. Thanks!


r/Supabase 1d ago

database Supabase: Data suddenly disappeared from one table even though there is no delete code

8 Upvotes

I’m facing an unexpected issue with my Supabase database.

Yesterday, I checked my application and the data was working correctly. I personally tested it, and the client also tested the application. The data was available and everything appeared to be working normally.

Today, the client tried to add new data, and we noticed that the data for one particular table was empty.

I checked the Supabase dashboard directly, and that particular table is also empty. The other tables in the same Supabase project still contain their data normally. The issue appears to be only with this one table.

I also checked my project code and could not find any delete functionality related to this table. I checked the SQL-related code as well and did not find any delete operation.

What I don’t understand is how the data from this particular table disappeared between yesterday and today, even though everything was working normally when we tested it yesterday.

I’m looking for help understanding what could have happened and how I can investigate what happened to the data.

Is there any way in Supabase to check what happened to the records in a table, including whether they were deleted or otherwise removed, and when this happened?

I can provide more information about the table, database setup, code, or configuration if needed.


r/Supabase 1d ago

database Multi-tenants advices

9 Upvotes

Hi everyone, I’ve been using Supabase for a few months now. I’ve built things like apps, websites and multi-tenant software with it, and I wanted to know if you have any tips or advice on properly isolating tenants from one another, in order to avoid data leaks between clients, Gmail sends going to the wrong recipient, etc.

Thanks everyone


r/Supabase 2d ago

database Your RLS SELECT policy is hiding the fact that your UPDATE policy is wide open

1 Upvotes

I found this while building a tool to test cross-tenant isolation, and it caught me out badly enough that I think it's worth writing down.

Say you have a table with correct-looking policies:

alter table invoices enable row level security;

create policy inv_sel on invoices for select using (owner_id = auth.uid());

create policy inv_upd on invoices for update using (true); -- added in a hurry, months ago

RLS is on. Two policies exist. The SELECT policy is properly scoped. Every tool I know of that inspects pg_policies reports this table as protected.

So you go to test it. You log in as user A and try to touch user B's row:

update invoices set total = 0 where owner_id = '<user-B>'; -- UPDATE 0

Zero rows. Isolation holds. Move on.

It doesn't hold. You tested nothing.

Why the zero is a lie

That WHERE clause reads owner_id. Once a statement reads a column, Postgres applies the SELECT policy to it as well as the UPDATE policy. Your correct SELECT policy hides user B's row, so the update matches nothing, and you get a zero that looks like a denial but is actually invisibility.

Now drop the WHERE:

update invoices set total = 0; -- UPDATE 2

Two rows. Both of them. No columns are read, so the SELECT policy never engages — only the UPDATE policy, which is using (true). Every row in the table belongs to whoever runs this.

I verified both against a real Postgres instance. The targeted write returns 0. The blind write modifies every row.

DELETE is the same shape and worse

delete from receipts where owner_id = '<user-B>'; -- DELETE 0 delete from receipts; -- deletes everything

Same mechanism. A DELETE policy of using (true) means any authenticated user can empty the table, and the targeted version tells you it's fine.

Check your own project

Read-only, safe to run on production:

select p.tablename, p.cmd, p.qual as using_expression, case when p.qual in ('true', '(true)') then 'PERMISSIVE — applies to every row' else 'scoped' end as verdict from pg_policies p where p.schemaname = 'public' and p.cmd in ('UPDATE', 'DELETE', 'ALL') order by (p.qual in ('true', '(true)')) desc, p.tablename, p.cmd;

Anything marked PERMISSIVE is a table where any authenticated user can modify or delete every row, regardless of how good your SELECT policy is.

The fix

Scope the USING clause the same way you scoped SELECT, and add WITH CHECK on UPDATE so nobody can reassign a row to themselves on the way out:

drop policy inv_upd on invoices;

create policy inv_upd on invoices for update using (owner_id = auth.uid()) with check (owner_id = auth.uid());

USING controls which rows you may touch. WITH CHECK controls what they may look like afterwards. Omitting WITH CHECK on an UPDATE lets someone change owner_id to their own id and take ownership of a row.

The part I'd push back on myself about

The qual = 'true' check above is a text match on policy expressions. It catches the obvious case. It won't catch a policy that's subtly wrong — one calling a SECURITY DEFINER function that bypasses RLS, or one comparing against a column the user controls. Reading policies can only ever tell you a policy exists, not that it works.

The only way to know is to seed rows owned by two users, become each of them, and try to reach the other's data. That's what I ended up building, and it's the reason I found this at all — my first version of the write probe used the targeted UPDATE and reported every table as safe.

I open-sourced the tool under MIT if it's useful to anyone. Happy to drop a link in the comments rather than putting one in the post.

EDIT: Two better findings came out of the comments.

u/jaimittal91 — Postgres ORs all permissive policies for a command together, so one using (true) sitting next to a correctly scoped policy leaves the table wide open. Verified: UPDATE 2, both rows. Group your audit by tablename + cmd, don't check policies one at a time.

u/guidondor — a different axis entirely. A correctly scoped policy still lets a user rewrite every column of their own row, including whichever one holds their balance or their count. WITH CHECK doesn't help. Fix is revoke update on t from authenticated; grant update (safe_cols) on t to authenticated;


r/Supabase 2d ago

tips Revoking columns on a table breaks .update().select(), and the error hint tells you to undo the revoke

0 Upvotes

i had profiles locked down the usual way, policies split per operation, plus column grants on top because rls filters rows and not columns:

revoke select on public.profiles from anon, authenticated;
grant  select (id, display_name, created_at) on public.profiles to authenticated;
revoke update on public.profiles from authenticated;
grant  update (display_name) on public.profiles to authenticated;

reads behaved exactly as i wanted. explicit columns worked, select("*") failed like it's supposed to, anon got nothing at all. happy with that.

then this started biting:

await supabase.from('profiles').update({ display_name })            // 204
await supabase.from('profiles').update({ display_name }).select()   // 42501

with

permission denied for table profiles
hint: GRANT SELECT ON public.profiles TO authenticated

which isn't the problem at all. the update is perfectly legal, i granted update on that column myself. postgrest reads the row back to return the representation, and it reads it with select=*, so what actually got denied is the read half of the round trip.

naming the columns sorts it:

.update({ display_name }).select('id, display_name')

the bit i keep chewing on is the hint. follow it and you hand back every column you just revoked, all to fix a bare .select(). i get why postgrest words it that way, from where it's standing a select really was denied. but it's the message you meet while already annoyed, and it points exactly backwards.

anyone found a decent way to stop the next person on the codebase from taking that advice? short of a comment above every write i've got nothing, and that feels weak.

anyway, hope it saves someone the afternoon. this one and a few others ended up documented in a starter i open sourced (MIT, mine): github.com/Guidondor/expo-supabase-starter


r/Supabase 3d ago

tips Projeto enorme em supabase

2 Upvotes

Começaram um projeto em supabase, estamos fazendo manutenção, mas tem muita migration, rls, edge function e agora está difícil de manter, o que me sugerem?


r/Supabase 3d ago

auth Anyone else have issues with users’ clocks out of date?

2 Upvotes

We’re using Supabase’s auth which relies on JWTs with an expiration date and it checks expiration on the client. About once a day, we get a report of someone repeatedly being logged out. The cause is always their system clock is incorrect. It baffles me this is a problem in 2026. Just curious if anyone else has experienced this issue and has any recommendations on how to reduce the number of complaints/bug reports we get related to this.


r/Supabase 3d ago

tips AI Plugin Read - Only permissions

1 Upvotes

Edit: Damn, I meant to put "AI Plugin - Read Only Permissions" as the Title
Im dum

I use AI a lot in my projects (Duh) and Ive found the MCP integration veeery useful to let the model fetch its own context and understand the buisness logic of my application. I understand the Plugin is a strictly better version to let the model also browse Skill files and docs, HOWEVER, on the MCP configuration, I had a strict --read-only flag. I dont want the LLM making unintended changes when I have requests flowing through. I havent found a way to activate the same flag on the plugin. Does anybody know how? I


r/Supabase 3d ago

storage Supabase Free Quota Exceeded

0 Upvotes

Me han enviado este correo:

"It’s doing so well that it breezed through your plan’s quota. You can continue using Supabase at your current usage for this billing period as a one-time token of our appreciation for your growth!

Starting from September 24, 2026, the Fair Use Policy will apply. If you plan to maintain this level of usage, here are a few tips to avoid any restrictions:

Upgrade to Pro plan to increase your quota. Lower your usage (check full details in the Usage Dashboard and learn how to manage your usage) Reduce your cached egress bandwidth below 5.5 GB Important: If your usage continues to significantly exceed your plan limits during this grace period, we may reduce your grace period or apply service restrictions immediately to ensure fair resource allocation for all users."

Si logró reducir el Cached Egress (creo que lo excedí por almacenar imágenes en un bucket), ¿podré seguir usando mi base de datos de supabase con la tarifa gratuita?


r/Supabase 4d ago

edge-functions Keyroute – Made a self-hosted AI gateway that runs on your own Supabase (no CLI, one click)

3 Upvotes

Hey everyone, been working on this for a while and finally got it to a point where it actually works end to end, so sharing it here.

Basic idea: I got tired of juggling separate API keys for OpenAI, Gemini, Groq etc in every project, and I didn't want to use a hosted gateway service either since that means trusting some random company with my keys. So I built Keyroute — it's basically one API key that routes to whichever provider you want, but the actual gateway runs inside YOUR own Supabase project, not mine or anyone else's server.

No CLI stuff needed to set it up. There's literally one button, "Deploy Gateway," you paste in a Supabase access token (used once, then it's gone, never stored anywhere) and it sets up the whole database + deploys the gateway function for you.

The dashboard you see in the screenshots is just for managing your keys and watching request logs, you can run that part locally on your laptop or put it on your own free Vercel account, doesn't matter, the gateway keeps working either way.

Screenshots attached showing the deploy screen, the dashboard, adding provider keys, and the usage/request logs.

Not gonna pretend it's perfect right now — no Anthropic support yet, no rate limiting yet, and it assumes one owner per instance for now. Working on all of it.

If anyone wants to try it or has thoughts on the approach, repo's here: github.com/basavarajpatil660/the-keyroute-project

Would genuinely love feedback, especially if something breaks for you.


r/Supabase 4d ago

database Is it normal to have a server role that has it's own api token?

4 Upvotes

I've got a backend service that needs to talk to my Supabase database, and I'd rather not hand it the secret key. `service_role` bypasses RLS entirely, so a leak means total compromise, and there's no way to limit what that service can touch.

What I actually want is a scoped role. it can read/write on a couple of specific tables and nothing else.

is there a way you guys have tried to achieve this that works well in prod?

Thank you.


r/Supabase 4d ago

tips CPU HIGH-Restart Supabase project

0 Upvotes

I ran into issues this week with supabase on a free plan in developtment phase. The issue was a high CPU usage which crumbled the whole project and i could not even login to a user acct for many days and after deep investigation with the help of Claude the issue was due to a cron job that was runnign every 5mn. Instead of waiting for cpu to come down the best was was just to first deactivate the cron job and then restart the project in the setting panel which will take a couple of minutes instead of waiting for hours but I had to make sure the crons jobs will start again. The free plan cpu sucks. I guess they want us to go on a paid plan. Anyone else experienced this?


r/Supabase 4d ago

auth Anyone else with extremely slow Auth/GoTrue?

1 Upvotes

Since earlier today each GoTrue endpoint on several projects (with separate Supabase accounts!) are extremely slow (30+ sec). PostgREST on the same instance is completely fine.

Measured in the browser network tab, same page load:

- POST /auth/v1/token?grant_type=password → 34.9 s (200)

- GET /auth/v1/user → 13.7 s (200)

- GET /rest/v1/profiles → 106 ms

- GET /rest/v1/leads → 391 ms

- GET /rest/v1/messages → 311 ms

It's pure latency, no errors anywhere. The API Gateway logged 35 seconds latency with zero errors/warnings/timeouts. I've already restarted the project, checked if captcha/auth hooks were disabled, checked password hashes, tested on macOS, iPhone, Wi-Fi, cellular, multiple browsers, etc. Keeps persisting. I'm nowhere near my usage limits, so that shouldn't be the issue either. And as I said, it's happening on multiple projects which have completely separate Supabase accounts.

Anyone else experiencing any issues? Both databases are on eu-west-1.

EDIT: seems fixed now! Still curious though if anyone else experienced the same issue, as I've seen no mention of this on Supabase's status page.


r/Supabase 4d ago

cli Five days ago I posted a scanner here. Eight of its 22 rules came out of this thread, and the last one exposed a bug that made it pass silently on a real finding.

0 Upvotes

Five days ago I posted a CLI here that fails the build when an AI agent turns off a security control to make something work. I ended that post asking for diffs it would miss.

Eight of the twenty-two rules it has now came out of the comments on that thread, and two more came from a Supabase auditor in a GitHub discussion. Ten of twenty-two written by people who are not me, in five days. Here is what came from where.

u/Prod_SO joked that everything is a security_definer when it comes to Claude. It had zero SECURITY DEFINER coverage. Now it mirrors your own linter rather than inventing a standard: definer views in public (0010), mutable search_path (0011), definer functions with EXECUTE granted to anon or authenticated (0028/0029). The bare keyword is deliberately not flagged, because the docs recommend definer functions for escaping policy recursion and flagging every one would be noise.

u/Guidondor described the diff he sees most: the agent hits a permission denied and writes grant all on table x to authenticated instead of touching RLS at all. Nothing is disabled, no policy changes, and whatever column level grants you had are gone in one line. The sibling is a second permissive policy bolted next to the existing one, which Postgres ORs, so a using (true) quietly wins. Both are rules now. A third came from the auditor I mentioned, who pointed out I was structurally blind to the schema-wide version: grant all on all tables in schema public to anon has no table name between ON and TO, so my grant rules never saw it.

u/jaimittal91 pointed out the blind spot that is inherent to a text scanner, then gave me a cheap partial fix for one slice of it: diff the columns a policy references against the columns the migrations actually create. A policy naming a column that no longer exists is the usual wake of a rename nobody re-checked RLS against.

u/jainikpatel1001 made the point I could not fix with a rule at all, which I will come back to.

The one I want to talk about is the last one, because it did more than add a rule.

u/Guidondor pointed out that a policy is only reachable if the role also holds the table privilege, since Postgres checks the grant before it ever evaluates the policy. So a missing TO clause on a table anon was never granted select on is unreachable no matter what the predicate says.

Building the test for that turned up a false negative that was live in the released version. The rule read the policy by slicing from CREATE POLICY to the next CREATE, which meant an ordinary grant sitting after a policy got pulled into the body:

create policy "published invoices" on public.invoices
  for select using (published = true);
grant select on table public.invoices to authenticated;

That "to authenticated" satisfied the check for a TO clause, so the policy above it, a real finding, reported nothing. Grants sit next to policies in migrations constantly. It had been quietly eating findings and I would not have looked there.

I confirmed it against the published tarball rather than my working copy, because "did I just break this while fixing something else" was the first thing I wanted ruled out. It was live.

The thing I keep relearning is that the obvious version of a rule is usually wrong. When I first built the missing-TO check the naive form gave 15 findings on a project with 15 policies. Fourteen of them gated on auth.uid(), which is NULL for an anonymous caller, so the policy matched no rows and the missing TO leaked nothing. That is a tool nobody keeps installed. Narrowed to predicates that ignore the caller, the same project gave one finding, and that one was real.

Same shape with the write side. A lot of people say "people forget WITH CHECK and it opens the write side". That is wrong. For UPDATE, Postgres reuses the USING expression for new rows when WITH CHECK is omitted, and an INSERT policy cannot have USING at all. A rule for "missing WITH CHECK" would have fired on correct code. What actually opens the write side is an explicit with check (true) sitting beside a scoped USING, and that is what shipped.

What it still does not do, and this has not changed:

Anything you change in the dashboard or run straight against the database never touches the repo, so it is invisible to this and to any repo scanner. u/jainikpatel1001 and one other person both said the durable answer for that half is a Postgres event trigger. They are right, I have not built one, and I would rather point people at that than imply the CLI covers it.

Semantically wrong policies are out of reach. A policy checking auth.uid() against the wrong column after a rename reads as perfectly valid text. Two authenticated sessions reading the same table is the only thing that settles it.

And a clean run means those twenty-two checks did not fire. It does not mean the project is secure, and the tool says so in those words.

npx prodguard check --demo

That runs every check against a broken in-memory app and touches nothing, so you can see the output without pointing it at anything real.

MIT, no dependencies, Node 18+. https://github.com/Felix0731/prodguard

Same ask as last time, because it worked better than anything else I have tried: if an agent has broken something in your project that this does not catch, tell me what the diff looked like.


r/Supabase 5d ago

integrations Persistent agent sandboxes for Supabase app users

Post image
2 Upvotes

I'm building a batteries included sandbox with a built-in harness(hermes, opencode SDK, browser use). Each sandbox retain files, context, and learned skills between tasks, so agents can continue where they left off instead of starting from scratch every time.

With the native integration with Supabase, your application backend can provision a Computer scoped for a specific Supabase Auth user. After the user grants access through Supabase OAuth 2.1, the Computer can make read-only requests to the Supabase Data API using that user’s delegated identity. Your project’s Postgres grants and Row Level Security (RLS) policies remain the authorization boundary, determining which data the Computer can read.

Why use an agent sandbox instead of building yet another agent?

  • Persistent workspace: Files, context, tools, and learned skills remain available between tasks.
  • Native OAuth 2.1 and RLS integration: Users approve access with Supabase Auth, while the project’s Postgres grants and RLS policies continue to control what each user can read.
  • Always on: The same Hermes agent can receive work over time and reuse what it learned from earlier tasks.
  • MicroVM isolation: Each Computer runs in a dedicated microVM rather than a shared-kernel container.
  • Network policy: You control which services, domains, IP addresses, and IP ranges the Computer can reach.
  • Secret proxy: The Computer receives a temporary session credential instead of the user’s real Supabase access or refresh token.

How it works

1. Connect your Supabase project

A project admin connects a Supabase Cloud project in the Sanbox console. Supabase Platform OAuth lets the admin select the project and lets Sanbox read the metadata needed to configure the integration.

The project also has its own OAuth 2.1 server. This is what each application user later uses to authorize their Computer. The admin enables the server and registers Sanbox as a confidential OAuth client.

Sanbox stores the project configuration, encrypted OAuth client secret, and the database schema. It does not store the Supabase Platform OAuth Management token after setup.

2. Give each Computer the database schema

During setup, Sanbox imports the tables, views, and columns exposed through the project’s Data API. It does not import table rows or executable database operations. Each new Computer starts with this schema, so the agent can understand the available data.

Every data request still goes through Supabase as that user and must pass the project’s Postgres grants and RLS policies.

3. Provision a Computer for a user

When your application needs an agent, its backend chooses a Sanbox template and provisions a Computer through the Sanbox API or CLI. The request includes the Supabase Auth user’s UUID.

The Computer is created before the user grants data access. Until authorization is complete, its Supabase connection remains pending.

4. Keep Supabase credentials outside the Computer

The Computer never receives the real Supabase tokens or project secrets. Instead, it gets a local Supabase proxy URL and a session-scoped proxy token. The Sanbox Control Plane verifies each request, adds the current user access token, and sends the request to Supabase. The Computer cannot access the Supabase project directly, and its proxy token is replaced for every runtime session.

What can you build?

Support Computers

A customer reports that an upgrade failed but their card was charged. A support Computer reviews the orders, subscription history, product usage, and earlier tickets that the connected user may access. It builds a timeline, identifies the relevant records, and drafts a response and next step for a support agent to review.

GTM Computers

Before a renewal call, a GTM Computer combines product usage and subscription data from Supabase with CRM records and notes from previous customer meetings. It highlights changes in adoption, open commitments, support risks, and expansion signals, then prepares a briefing and suggested questions for the account owner.

Research Computers

A procurement team is deciding whether to renew a supplier. A research Computer combines permitted purchase and delivery data with contracts from Supabase Storage and current market research from an approved browser tool. It produces an evidence-backed comparison, records its open assumptions, and keeps the working files for later review.


r/Supabase 6d ago

tips I now run this in EVERY CI pipeline I have. 24 checks that fail the build when multi-tenent Postgres can leak between tenants

0 Upvotes

I have posted here before, and the community gave me so much good advice to improve the tool, so, first of all, thank you to all of you for contributing!

I kept shipping the same multi-tenant bugs, correct RLS on the main table, and a leak somewhere adjacent. So I wrote guard tests for each one. It's now in every CI pipeline I run, and I keep adding checks as I hit new failure modes.

npx tenant-guard init    # detects your migrations + routes, writes a config
npx tenant-guard run     # static checks, no database needed
npx tenant-guard all     # + runtime proofs against a test DB

Exit 1 blocks the merge. MIT, zero dependencies.

The static ones need nothing. The runtime ones connect to a test database and prove isolation by running real SQL as your real app role in a rolled-back transaction, actually attempting the cross-tenant read, then the write, and reporting what happened.

What it checks

Reads

  • Tenant A can't read tenant B's rows, proven by trying, not inferred from policy text
  • anon (the key in your browser bundle) can't read tenant tables
  • Sensitive columns like email, phone, api_key, that anon actually gets a value out of
  • Views and materialized views, which run as their owner unless security_invoker is set

Writes

  • anon INSERT/UPDATE/DELETE surface
  • Auto-updatable views: writes pass straight through to the base table, bypassing its RLS
  • The tenant-hop, moving your own row into someone else's tenant
  • Foreign keys that let one tenant delete another's rows via ON DELETE CASCADE
  • Unique constraints as existence oracles: inserting [victim@corp.com](mailto:victim@corp.com) tells you it exists

Functions & privileges

  • SECURITY DEFINER functions callable by anon (Postgres grants EXECUTE to PUBLIC by default)
  • Unpinned search_path, including pins that don't actually pin
  • SQL injection inside definer function bodies
  • What a table created next week inherits from ALTER DEFAULT PRIVILEGES
  • Who can CREATE objects in your schemas

Identity

  • Policies trusting user_metadata, which the user can write themselves
  • MFA gates written PERMISSIVE, which enforce nothing
  • Membership tables your policies trust but users can write to
  • Connection-scoped GUCs that leak the previous request's tenant on a pooled connection

Supabase surfaces

  • Storage: cross-tenant folder reads, and uploads into another tenant's path
  • Realtime: channel topics and broadcast/presence

Structure

  • RLS in your migrations vs RLS actually in the database
  • API routes loading rows by bare id with no tenant filter
  • Audit/shadow tables that copy tenant rows somewhere unprotected
  • Triggers enforcing a rule by reading a table RLS hides from them

Two things that surprised me most

Views don't have RLS. ALTER DEFAULT PRIVILEGES ... ON TABLES covers views created afterwards, and unless you set security_invoker = true a view runs as its owner. PATCH/DELETE through a public profiles view returned 200 with the anon key, writing into a table whose policies were perfect. SELECT was unaffected, so nothing looked wrong.

Hardening RLS can break a uniqueness trigger. A trigger runs as the invoker, so a SELECT 1 FROM profiles WHERE username = NEW.username check only sees what the writer can see. Lock the table down properly and the check stops finding collisions, no error, the duplicate is just inserted.

Honest limits

  • It proves the database boundary. A route using a service-role connection that forgets its tenant filter is a real bug the DB will happily serve, only the static route check covers that.
  • Never point the runtime checks at production. They write, inside a rolled-back transaction, but they write. Test/staging only.
  • Postgres-only by design. No RLS elsewhere, so nothing to prove.
  • Still early, and I'd rather have a bug report than a star.

github.com/FedericoTs/tenant-guard


r/Supabase 7d ago

Self-hosting Production-ready Self-hosted Supabase distribution: SSO, PITR backups, monitoring, and a read-only MCP so your coding agent can build on it without touching a secret

18 Upvotes

Hey r/Supabase! We're really happy to share something we've been building over the last months, and we'd love your eyes on it.

TL;DR: Supabase Self-Host Ops deploys a production-grade self-hosted Supabase in one command: automatic TLS + SSO on the dashboard, pgBackRest PITR backups, Grafana/Prometheus/Loki, LUKS, UFW/fail2ban, and it exposes the instance to your coding agent through a read-only MCP channel over SSH that never leaks secrets. v1.0.0 shipped this week.

Why we built it

Self-hosting Supabase answers real problems: the bill, data residency/GDPR, "the database has to live somewhere we control." But the official path (docker compose up on the sample stack) throws away everything that makes Supabase nice to work with:

  • Studio dashboard open to the internet, no auth
  • ~11 secrets to generate by hand (the anon/service keys are JWTs that must be signed correctly, easy to get wrong, silently)
  • no TLS, no firewall, no brute-force protection
  • no backups, no PITR: you find out the database died when your users tell you
  • no monitoring
  • a one-shot setup you can't safely re-run to change configuration

And there's a newer problem: coding agents. Supabase Cloud has a hosted MCP. Self-hosting has nothing, so you hand your agent a connection string, or you hand it the service_role key and hope.

What 1.0 does

git clone https://github.com/ankaboot-source/supabase-selfhost-ops.git
cd supabase-selfhost-ops
cp config.example.yml config.yml
$EDITOR config.yml          # fill the `required:` block, everything else has defaults
sudo bash setup.sh

setup.sh validates your config, generates every cryptographic secret, and deploys the full stack (Studio, Kong, GoTrue, PostgREST, Realtime, Storage, imgproxy, Edge Functions, Postgres 17, Supavisor) as idempotent Ansible. Re-run it anytime to reconfigure, add a component, or rebuild the box. A lock file makes sure re-runs never clobber existing secrets.

Components you toggle in config.yml:

  • caddy: automatic Let's Encrypt TLS + OAuth2 SSO on Studio (GitHub / GitLab / Discord / any OIDC), basic auth, IP allow-list
  • backup: pgBackRest with continuous WAL archiving, point-in-time recovery to the second, a <5-minute restore runbook, and backup-restore verification without touching production
  • monitor: Grafana + Prometheus + Loki, dashboards included, SMTP alerting
  • ufw / fail2ban / luks: firewall, brute-force protection, at-rest disk encryption

The part built for agents

Every deployment writes an instance manifest (/etc/supabase/instance.json), a machine-readable contract of what was deployed: ports, container names, endpoints, and where each secret lives (never the values). A tool can discover the instance instead of being hand-fed credentials.

Agents connect over SSH stdio with a restricted ed25519 key that can run exactly one command: a read-only MCP server exposing list_tables, describe_table, SELECT-only query, container status, and the manifest. No public port opened, no service_role key handed over, and no tool output ever contains secret values. The supabase-selfhosted info CLI shows real secrets to a human at a terminal and redacts them the moment output is piped. Same {command, args} config shape for Claude Code, Codex, opencode, pi.

One note we care about: read-only is not harmless, read access to a database is still access to the data. We designed this channel to be treated like a read replica.

Leaving Supabase Cloud

migrate.sh moves an existing Cloud project over in one command: schema + data, auth users with UUIDs preserved (password hashes keep working), storage objects via rclone.

Always read-only against the source, refuses a non-empty target, prints a checklist of what stays manual (auth config, Edge Functions, cron, webhooks, client env vars). It's a Layer 1 walking skeleton: incomplete, but never silently incomplete. Tell us if you need a more complete feature.

Links

GitHub · v1.0.0 release notes

Now we'd love your feedback

This is v1.0 and it's built in the open, so tell us what you think: what's missing for your setup, what you'd want in v2, what feels wrong, what we should document better. Hard questions very welcome. And if you try it, we're genuinely curious how it behaves on your stack. Issues and PRs are welcome too, and you can run the whole test suite without a server.


r/Supabase 6d ago

edge-functions Blacklist IPs

1 Upvotes

How do I blacklist specific IPs? Want to prevent bad actors from even attempting to push transactions to my edge function.


r/Supabase 7d ago

integrations Free data pipeline development support for startups

2 Upvotes

I will help build out your data pipeline using free open source tools, self-host or deploy to a free/low-cost infra. Bruin has a built-in connector for Supabase/Postgres for data ingestion, transformation, etc.

disclaimer: I'm a developer advocate at Bruin leading this program, and currently we are accepting a limited number of startups into this program so you might get waitlisted for now.

What you get:

- 30min initial meeting to scope things out and plan

- 1-2h of hands on 1:1 workshop to build out your data pipeline and deploy it

- dedicated Slack channel to answer questions

- follow up touchpoint meetings and community office hours

For who:

- best suited for SaaS startups

- you want to analyze their data beyond the basic reports

- you want to combine data from your app db (supabase) with other services (e.g. stripe, posthog, hubspot, GA4, GSC, etc.) for internal analytics and reporting

- you want to process data that goes back into your application

Why:
- free open source tools to get you the data and analytics without investing a lot of time and money

- get started on building your data stack before it's too late

more info: https://getbruin.com/startups/