Skip to content
Self-Hosting

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 as SET statements, 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:

1
psql 'postgres://postgres.[POOLER_TENANT_ID]:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres'

For transaction-mode connections:

1
psql '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:

VariableDefaultDescription
POSTGRES_PORT5432Host port for session-mode connections.
POOLER_PROXY_PORT_TRANSACTION6543Host port for transaction-mode connections.
POOLER_DEFAULT_POOL_SIZE20Postgres connections the pooler opens per pool. Keep this below your Postgres max_connections minus connections reserved for other services.
POOLER_MAX_CLIENT_CONN100Client connections the pooler accepts.
POOLER_TENANT_IDyour-tenant-idSupavisor tenant identifier, used in the username.
POOLER_DB_POOL_SIZE5Internal metadata pool used by Supavisor itself.

To check your current Postgres max_connections setting:

1
docker 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:

1
sh run.sh config add pgbouncer
2
sh run.sh start

If 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 PgBouncer
2
psql '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.

If you use the default Supavisor stack, edit docker-compose.yml:

  1. Disable Supavisor by commenting out or removing the entire supavisor service section.
  2. Expose the Postgres port by adding the port mapping to the db service:
docker-compose.yml
1
db:
2
ports:
3
- ${POSTGRES_PORT}:${POSTGRES_PORT}
4
container_name: supabase-db

If you use the PgBouncer override, Supavisor is already disabled. Uncomment the db block in docker-compose.pgbouncer.yml instead:

docker-compose.pgbouncer.yml
1
db:
2
ports:
3
- ${POSTGRES_PORT}:${POSTGRES_PORT}

After restarting, connect directly with a standard Postgres connection string:

1
postgres://postgres:[POSTGRES_PASSWORD]@[your-server-ip]:5432/[POSTGRES_DB]

Additional resources#