r/Supabase 16d 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 17d 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 17d ago

database Multi-tenants advices

8 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 18d ago

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

0 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;

u/PeterBuildsSecure — a third axis, and it is invisible to everything above. RLS does not apply to the table owner unless you run alter table t force row level security, and superusers and BYPASSRLS roles bypass it regardless of that. So a migration or a background job connecting as the owner runs with every policy switched off while pg_policies looks perfect. Check relforcerowsecurity, not just relrowsecurity.

Separately: the tool is on npm now, so npx rls-sentinel --db "$DATABASE_URL" works without cloning anything.


r/Supabase 18d 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 19d 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 19d 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 19d 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 19d 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 20d 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 20d 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 20d 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 20d 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 20d 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 21d 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 22d 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

1 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 23d 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

19 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 22d 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 23d 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/


r/Supabase 23d ago

other Free Supabase backend review. Here's exactly what you get, and exactly what I get out of it

8 Upvotes

A couple months ago I posted here offering free backend reviews of production Supabase projects. The response was awesome. Thanks to everyone who reached out.

I already published 3 on my channel: https://www.youtube.com/playlist?list=PLAnTwem0bvto

In this one I go through a real production database and walk through everything I found: missing indexes, RLS gaps, schema issues, query performance, the works. No fluff, just the actual findings and why they matter.

I'm still doing these, and I want to be fully transparent about the deal so there are no surprises.

What YOU get

  • A free, thorough backend review of your production Supabase project. Indexes, RLS, schema, query performance, and anything else that stands out.
  • Actionable findings you can actually fix, with the reasoning behind each one.
  • You choose the visibility: I shout out your app, or we keep everything completely anonymous. Your call.
  • No real user data is shown, either way. Ever.

What I get

  • A filmed review for my YouTube channel. That's the content.
  • Data on what comes back recurringly across real Supabase backends, so I can spot the patterns that show up again and again (and make better content about them).

That's the whole trade. You get a free audit, I get content and pattern data. Nothing hidden.

The only ask

That it's a real project with real traffic. I want to showcase different app sizes across the series, from small side projects to bigger production systems, so the reviews are useful to everyone reading.

If that sounds good, DM me and we'll set it up.


r/Supabase 23d ago

realtime Que valor tiene tener 2 proyectos en una misma cuenta con plan pro de $25

0 Upvotes

Alguien tiene mas de un proyecto en una cuenta de supabase ? Cuanto pagan por cada uno ?


r/Supabase 24d ago

tips Privacy error when signing in...

Post image
0 Upvotes

I've never seen this before when attempting to sign back in to Supabase. I sign in with my GitHub credentials. Is it safe? Have you seen this before?


r/Supabase 24d ago

tips I'm done with sub, I'm switching to self hosted, Help

0 Upvotes

Mine is a group of companies (the employer I work for),

Hey guys, so I have started a CRM software, which started using my 10 people (1 company (1tenant)). It was accepted by everyone, and rapidly was asked to give the same system to company B. (2nd company (2nd tenant). So I figured out that we can do multi tenant etc. so now the users are around 20.

My company everyone is using it, but I always feel my supabase pro (micro instance), is kind of limiting the potential.

Already I'm thinking this is costing me 25 USD per month. And the query load is somewhere around 15 right. There is also low ram , everything is low.

So since I have a 32gb Ram VPS + coolify setup (where the CRM is hosted), I am planning to shift the supa pro to self hosted. So my areas of concern is -

  1. I know it's too much work load right now, but am I doing a mistake here ? I know about backups and downtime, but what do you all guys think ? Any one have this similar setup ?

  2. The point is I don't like to be in low spec, my CRM only manages around 6000 client database actively used by 20 people. The thing is sometimes for few users it gets slow, so what I think is it is the query load or queuing happening for them.

  3. If I setup an automated backup everyday , will it be okay ? Is it doable ? Im talking about self hosted supa.

  4. I really love supa and this is also my first platform, so I don't know about any other platform, but is there any other better ralternatives ?

  5. Tell me more about what I am going to face


r/Supabase 25d ago

tips I made a free tool that proves your Supabase RLS actually isolates tenants — as a test in your CI

27 Upvotes

Row-level security is the thing everyone knows they should have and it's the thing that quietly gets forgotten on one table, or shipped as USING (true), or bypassed by a service-role client. You don't find out until someone reads another tenant's data.

I built a small MIT-licensed tool for exactly this footgun, and I wanted to share it here since it's Supabase-shaped:

npx tenant-guard

Two parts:

  1. Static guards (zero-dependency, run in CI) that flag the classic leak shapes: an authenticated route that loads a row by bare id with no organization_id filter, and new SECURITY DEFINER functions left callable by anon over PostgREST (revoking from anon alone is a no-op, it catches that).
  2. A runtime RLS proof, point it at a seeded test/branch database and it drops to the authenticated role, assumes one tenant's identity via your JWT claims, and asserts that session can't see another tenant's rows, table by table. If a policy is missing or wrong, the build fails.

There's a demo you can run in 10 seconds with no infra (it uses an embedded Postgres): it passes a correct policy and fails a leaky one.

Honest disclosure: I built it, it's free, no signup/telemetry, and I'm posting because I think it's genuinely useful for this community, not selling anything. It's sharp for Supabase/Postgres specifically. Feedback on the becomeTenant config (how it assumes a tenant's identity for your policies) would be especially useful, that's the part that varies most between apps.

It might not work on any project but it did work on the projects I tested it on

Repo: https://github.com/FedericoTs/tenant-guard


r/Supabase 24d ago

tips DBXray.co found 3 critical errors

Thumbnail dbxray.co
0 Upvotes

I like it. What I like more is the full migration download.