# 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.

- URL: https://blog.userelay.sh/guides/how-to-use-supabase-with-relay
- Author: Relay Engineering
- Published: 2026-11-10
- Updated: 2026-11-10
- Tags: supabase, postgresql, relay cli, masking

## Short answer

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:

```bash
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:

```sql
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.

> **Row-level security.** `relay_ro` is an ordinary role, so row-level security applies to it. On a table with RLS on and no policy for `relay_ro`, its queries return zero rows. Add a policy, such as `create policy "relay read" on public.orders for select to relay_ro using (true);`, for each table it should see.

## 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:

```bash
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:

```bash
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?

```bash
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:

```bash
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?

```bash
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`.

