·
· · ·
·
· ·
++++++++++++++
+++############+++
· ++################++ ·
· · · · ++##################++ ·
·
· · ++####################++ ·
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
······· ············ ················ ········ · ·········· · ······· = = = ==···=···=··············· ············ ·
·············-·· ···-···-···· ····--··· ············· ····-·-···- - --····-···--··-·············· ············ · ·
············ ····---···· ·····------·· ····-·-----··· ······------· == ·=·=====-=····=···········-·· ·--·-········· ·
··------·· ·····--------····· ···· · ······· ·· ···----------··· ····---------··· - -·-·--------····-····· ········ ·-----······· ·
·---········------·· ····-----=--------··· ··-----------=---=-----==--=----···· · ·-·--=-=-----···· =··=-===--=·=·= ==· ····· ·----···· ···
=--===-==-=---· ···-====------····· ··--====-------------------=----···· ····---=--==--·-·· ··---=---------- -- ··-=-···· ·
· ·---====---· ····---=====-··· · · ·· ·· ···· ··---=======----···· ·--========·= === --==-· ··
··· · ·· ···========-··· ··-·---=====+=====-· · ·-·--=========--·· ··---=++=----··-- - ·-===···
+++++=++=+====+--·· ·--======+==+=+++===---··· ····--···--···--··-· ··-·--=-====+++===----· · ···-----==+==-=·== = ==··==== · · ·===--· ·
-···-·------· · ·--==++++===-==----·· ·-====++++++++++++===+=======+=+++++=+++=-··· ·· ····-·---=+++++=+=-·· - ---- -------··-··--··-·· ··=++=· ·
··===+*++++---···· ·····-===+*++=====-====++=====+++++*+++++===-=-·· ···-==+=+++++++**+=-·· == =·===-=======--=====--··· ··-+++=· · -
+*++==+=--==++*+++=-=-· ·····-·---=====*++*++*+==-··· · · · -----=----·· · ·----===*++**++++++==--· · -----==-*-++*+++++*+++===----==+*=- ·-
=+==++++***++==·· ····==++*+*+****+*+*++++==-==-· · · ··--==+++*+****+*++====---·-· ··-====****==*=***+*********++=+=++++=· ·· ···· ···· ···-·How to use Supabase with Relay
Reach a Supabase Postgres database through Relay: a dedicated role, the session pooler, verified TLS, row-level security and masking at the wire.
- Author
- Relay Engineering
- Published
- Updated
- Reading time
- 4 min
Create a dedicated Postgres role in Supabase, register the session pooler address as a Relay connection with Supabase's CA certificate, and open it with relay connect. Clients and agents use a local port, row-level security still applies to the role, and Relay masks and audits every statement on the way through.
A Supabase project is a Postgres database with a lot around it: Auth, Storage, the Data API. Relay only needs the database part. This guide gives Relay its own role, connects through Supabase’s session pooler with verified TLS, and turns on masking, so people and agents can query the project without holding its password.
What do I need before I start?
- A Supabase project, and access to its SQL editor and database settings.
- The Relay CLI, signed in:
brew install relay-sh/tap/relay
relay login
Which role should Relay use?
Create one for it. In the Supabase SQL editor, a read-only role for the public schema looks like this:
create role relay_ro login password 'use-a-long-generated-password';
grant usage on schema public to relay_ro;
grant select on all tables in schema public to relay_ro;
alter default privileges in schema public grant select on tables to relay_ro;
The alter default privileges line covers tables created later. Add a second role with write grants only if someone, or some agent, really needs to write through Relay.
Which connection string should I use?
Open Connect in the Supabase dashboard. It lists three ways in:
| Option | Port | Notes |
|---|---|---|
| Direct connection | 5432 | db.<project-ref>.supabase.co. IPv6 unless the project has the IPv4 add-on. |
| Session pooler | 5432 | <region>.pooler.supabase.com through Supavisor, over IPv4. |
| Transaction pooler | 6543 | Same host. One server connection per transaction, no prepared statements. |
Use the session pooler unless you know the Relay Agent’s network has IPv6. A Relay session is an interactive connection that may run many statements and use prepared statements, which is what session mode is for; transaction mode is meant for short-lived serverless functions.
Through the pooler, the username carries the project reference: relay_ro.<project-ref>.
How do I get TLS right?
Supabase signs its database certificates with its own root CA. Download it from the database settings (SSL Configuration), then pass it to Relay so the agent verifies the server instead of trusting anything:
read -rs SUPABASE_PASSWORD # the relay_ro password
relay connections create \
--name supabase-prod \
--engine postgres \
--host aws-0-us-east-1.pooler.supabase.com:5432 \
--user relay_ro.<project-ref> \
--password "$SUPABASE_PASSWORD" \
--database postgres \
--host-tls-ca-file ./prod-ca-2021.crt
unset SUPABASE_PASSWORD
Use the pooler host and CA file name your project shows. TLS to the database is on by default; --host-tls-ca-file tells the agent which CA to verify against. Avoid --host-tls-skip-verify: the Relay server refuses it for managed agents reaching public hosts anyway.
How do I mask user data?
Supabase projects tend to keep personal data in public tables next to the app’s own. Turn on redaction and automatic detection, then add rules for the columns you know:
relay connections update supabase-prod --redact-enabled
relay connections masking set supabase-prod --auto-mode strict --locale en-US --locale pt-BR
relay connections masking rules add supabase-prod --table public.profiles --column email \
--strategy partial --partial-show 3
relay connections masking rules add supabase-prod --table public.profiles --column phone \
--strategy hash
strict masks by column name in the given locales as well as by value, which helps with tables full of free-form names like full_name or cpf. When the classifier finds a column you have not covered, it shows up in relay connections masking review, where accept turns it into a rule.
How do I connect?
relay connect supabase-prod
supabase-prod (postgres)
Listening: 127.0.0.1:5432
...
Connect with:
psql "postgresql://:noop@127.0.0.1:5432"
The credentials shown are placeholders the proxy ignores.
If you run Supabase locally with the Supabase CLI, its database already uses port 5432, so relay connect moves to the next free port and tells you which. Pin one with --port 6432.
To give the connection to a team, or let someone ask for it for a limited time:
relay grants create --grantee-type team --grantee data --target-type connection --target supabase-prod
relay grants request supabase-prod --for 2h --reason "Investigate signup drop"
How do I check what ran?
relay audit-logs --connection supabase-prod --limit 20
Each entry is tied to the person who opened the session, not to relay_ro, so a shared role no longer hides who ran what.
Questions
Does Relay sit in front of the Supabase REST API or supabase-js?
No. Relay works at the Postgres wire protocol, so it covers psql, ORMs, drivers and agents that open a database connection. Requests through the Supabase Data API go to Supabase directly and are governed by its API keys and row-level security.
Can I use the postgres user instead of a dedicated role?
You can, but you should not. A dedicated role lets you grant exactly the schemas and tables Relay may reach, and keeps row-level security in force. Relay policies and masking add to the role's limits, they do not replace them.
Why does the connection fail with a certificate error?
Supabase signs its database certificates with its own root CA, which is not in the system trust store. Download the CA from the database settings and pass it with --host-tls-ca-file.