Accessing Postgres
Connect to your self-hosted Postgres database through the Supavisor or PgBouncer pooler, or with a direct connection.
This guide explains how to connect to Postgres in self-hosted Supabase, using the Supavisor pooler, the optional PgBouncer pooler, or a direct connection.
Self-hosted Supabase uses Supavisor as its default connection pooler. A pooler sits in front of Postgres and shares a small set of database connections across many clients, which avoids exhausting Postgres connection limits.
Choose a connection mode#
Self-hosted Supabase offers three ways to reach Postgres:
- Session mode - Supavisor on port
5432. Best for persistent clients that need per-session features such asSETstatements, prepared statements,LISTEN/NOTIFY, or advisory locks. Each client holds a dedicated Postgres connection for the life of the session. Available by default. - Transaction mode - Supavisor or PgBouncer on port
6543. Best for serverless or edge functions that open many short-lived connections. Does not support session-level features (SET,LISTEN/NOTIFY, temporary tables that span transactions, or advisory locks). Supavisor pooler does not support prepared statements; PgBouncer can be configured to support them. Available by default. - Direct connection - Postgres bypassing the pooler. Not exposed by default - refer to exposing Postgres. Best for migrations,
pg_dump, and long-lived backends.
Connect through Supavisor #
Use your domain name, your server IP, or localhost, depending on where the stack runs.
For session-mode connections:
1psql 'postgres://postgres.[POOLER_TENANT_ID]:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres'For transaction-mode connections:
1psql 'postgres://postgres.[POOLER_TENANT_ID]:[POSTGRES_PASSWORD]@[your-domain]:6543/postgres'Supavisor requires the "tenant ID" (your-tenant-id) for authentication, not only the role. When using psql with command-line parameters instead of a connection string, the -U parameter must also be postgres.[POOLER_TENANT_ID].
Customize Supavisor#
Configure Supavisor settings through your .env file, then recreate the stack for changes to take effect:
| Variable | Default | Description |
|---|---|---|
POSTGRES_PORT | 5432 | Host port for session-mode connections. |
POOLER_PROXY_PORT_TRANSACTION | 6543 | Host port for transaction-mode connections. |
POOLER_DEFAULT_POOL_SIZE | 20 | Postgres connections the pooler opens per pool. Keep this below your Postgres max_connections minus connections reserved for other services. |
POOLER_MAX_CLIENT_CONN | 100 | Client connections the pooler accepts. |
POOLER_TENANT_ID | your-tenant-id | Supavisor tenant identifier, used in the username. |
POOLER_DB_POOL_SIZE | 5 | Internal metadata pool used by Supavisor itself. |
To check your current Postgres max_connections setting:
1docker compose exec db psql -U postgres -c "SHOW max_connections;"To change max_connections or other Postgres settings, refer to custom Postgres configuration.
For the full list of Supavisor's configurable environment variables, check the reference list in docker/CONFIG.md.
Use PgBouncer instead of Supavisor#
Self-hosted Supabase includes an optional PgBouncer override. It disables Supavisor and runs PgBouncer in transaction mode on POOLER_PROXY_PORT_TRANSACTION.
Add it to your stack with run.sh:
1sh run.sh config add pgbouncer2sh run.sh startIf you prefer to run Docker Compose commands explicitly, use docker compose -f docker-compose.yml -f docker-compose.pgbouncer.yml up -d.
To connect as postgres:
1# tenant ID isn't required for PgBouncer2psql 'postgres://postgres:[POSTGRES_PASSWORD]@[your-domain]:6543/postgres'The PgBouncer override provides transaction mode only. For session-mode connections, or for features that transaction mode does not support (such as SET statements or LISTEN/NOTIFY), reconfigure PgBouncer manually by editing its environment variables in docker-compose.pgbouncer.yml, or use a direct connection. PgBouncer reuses the POOLER_DEFAULT_POOL_SIZE and POOLER_MAX_CLIENT_CONN values from your .env configuration.
Expose Postgres for direct connections#
In the default configuration, Postgres is only reachable through the pooler. To bypass the pooler for migrations, pg_dump, or other direct-connection needs, expose the Postgres port.
Exposing Postgres opens your database to the network. Configure firewall rules or network policies to restrict access to Postgres.
If you use the default Supavisor stack, edit docker-compose.yml:
- Disable Supavisor by commenting out or removing the entire
supavisorservice section. - Expose the Postgres port by adding the port mapping to the
dbservice:
1db:2 ports:3 - ${POSTGRES_PORT}:${POSTGRES_PORT}4 container_name: supabase-dbIf you want to keep Supavisor running alongside a direct connection, map Postgres to a different host port (for example, 5433:${POSTGRES_PORT}) instead of disabling Supavisor.
If you use the PgBouncer override, Supavisor is already disabled. Uncomment the db block in docker-compose.pgbouncer.yml instead:
1db:2 ports:3 - ${POSTGRES_PORT}:${POSTGRES_PORT}After restarting, connect directly with a standard Postgres connection string:
1postgres://postgres:[POSTGRES_PASSWORD]@[your-server-ip]:5432/[POSTGRES_DB]