Files
supabase/apps/docs/content/guides/database/connecting-to-postgres.mdx
Miranda Limonczenko 931ebfbcaf docs: use an opaque placeholder for the pooler host
The rest of this PR aligns placeholders with what the Connect dialog
emits. `aws-[REGION]` didn't: it reads as composable and isn't. Hosts are
`aws-<index>-<region>.pooler.supabase.com`, and the index is a pooler
cluster index, not part of the region. Both aws-0-us-west-1 and
aws-1-us-west-1 appear in this repo, so a reader can't derive it, and
Studio doesn't compose the host either. It comes from the API.

- Use `[POOLER-HOST]` in the copyable strings, the convention the newer
  quickstarts already use.
- Keep the full shape in the summary table, where showing the shape is
  the point, and name the index there.

Composing the host produces `Tenant or user not found`, which reads like
a credentials error. Naming the index and explaining how to avoid it
follows in a later PR of this stack.
2026-09-02 14:12:07 -07:00

342 lines
19 KiB
Plaintext

---
title: 'Connect to your database'
description: 'Connect to Postgres from your frontend, backend, or serverless environment'
subtitle: 'Supabase provides several ways to connect to your Postgres database, whether your code runs in the frontend, in a persistent backend, or in a serverless function.'
---
## How to connect to your Postgres databases
How you connect to your database depends on where your code runs:
- For frontend applications, use the [Data API](#data-apis-and-client-libraries).
- For Postgres clients, use a connection string:
- Use the [direct connection string](#direct-connection) for single sessions and Postgres native commands. This covers database GUIs, client applications such as [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html), [migrations](/docs/guides/deployment/database-migrations), [backup and restore](/docs/guides/platform/migrating-within-supabase/backup-restore), and [replication](/docs/guides/database/postgres/setup-replication-external). The direct endpoint is on IPv6, or on IPv4 if the project has the [IPv4 add-on](/docs/guides/platform/ipv4-address).
- Use [pooler session mode](#pooler-session-mode) for application traffic from persistent backends on IPv4-only networks.
- Use [pooler transaction mode](#pooler-transaction-mode) for application traffic from short-lived clients, such as serverless and edge functions.
The following table summarizes each mode, its host and port, the IP version it supports on each plan, and what it's best used for:
| Mode | Host:Port | Free | Paid | Paid + IPv4 add-on | Best for |
| ---------------------------------- | ----------------------------------------------- | ---- | ---- | ------------------ | ------------------------------------------ |
| Direct connection | `db.[PROJECT-REF].supabase.co:5432` | IPv6 | IPv6 | IPv4 | Migrations, `pg_dump`, persistent backends |
| Shared pooler, session mode | `aws-[INDEX]-[REGION].pooler.supabase.com:5432` | IPv4 | IPv4 | IPv4 | Persistent backends on IPv4-only networks |
| Shared pooler, transaction mode | `aws-[INDEX]-[REGION].pooler.supabase.com:6543` | IPv4 | IPv4 | IPv4 | Serverless and edge functions |
| Dedicated pooler, transaction mode | `db.[PROJECT-REF].supabase.co:6543` | - | IPv6 | IPv4 | High-performance app traffic on paid plans |
<Admonition type="caution">
The IPv4 add-on is not dual-stack: enabling it swaps the project's IPv6 (AAAA) DNS record for an IPv4 (A) record, so the project endpoint becomes reachable only over IPv4.
</Admonition>
## Quickstarts
<div className="grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-6 not-prose">
<NavData data="ormQuickstarts">
{(data) =>
data.items?.map((quickstart) => (
<Link key={quickstart.url} href={quickstart.url} passHref>
<GlassPanel
key={quickstart.name}
title={quickstart.name}
className="[&>div]:p-2 flex justify-center [&_p]:text-foreground-light"
/>
</Link>
))
}
</NavData>
<NavData data="guiQuickstarts">
{(data) =>
data.items?.map((quickstart) => (
<Link key={quickstart.url} href={quickstart.url} passHref>
<GlassPanel
key={quickstart.name}
title={quickstart.name}
className="[&>div]:p-2 flex justify-center [&_p]:text-foreground-light"
/>
</Link>
))
}
</NavData>
</div>
## Data APIs and client libraries
The Data APIs let you interact with your database using REST or GraphQL requests. You can use these APIs to fetch and insert data from the frontend, as long as you have [Row Level Security](/docs/guides/database/postgres/row-level-security) (RLS) enabled.
- [REST](/docs/guides/api)
- [GraphQL](/docs/guides/graphql/api)
For convenience, you can also use the [Supabase client libraries](/docs/reference), which wrap the Data APIs with a developer-friendly interface and handle authentication for you:
- [JavaScript](/docs/reference/javascript/introduction)
- [Flutter](/docs/reference/dart/introduction)
- [Swift](/docs/reference/swift)
- [Python](/docs/reference/python/introduction)
- [C#](/docs/reference/csharp/introduction)
- [Kotlin](/docs/reference/kotlin/introduction)
## Direct connection
The direct connection string connects directly to your Postgres instance. Use it for persistent backends, such as virtual machines (VMs) and long-running containers. Examples include AWS EC2 machines, Fly.io VMs, and DigitalOcean Droplets.
<Admonition type="caution">
Direct connections are on IPv6, or on IPv4 if the project has the [IPv4 add-on](/docs/guides/platform/ipv4-address). If your network is IPv4-only and you don't have the add-on, use [pooler session mode](#pooler-session-mode) instead.
</Admonition>
The connection string looks like this:
```txt
postgresql://postgres:[YOUR-PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres
```
Get your project's direct connection string from the Supabase Dashboard by clicking [Connect](/dashboard/project/_?showConnect=true).
## Poolers
Supabase offers two poolers. The shared pooler, [Supavisor](https://github.com/supabase/supavisor), is multi-tenant, available on every project, and IPv4-only. The dedicated pooler, [PgBouncer](https://www.pgbouncer.org/), is available on paid plans and runs alongside your Postgres instance. Like the direct connection, it is on IPv6, or on IPv4 if the project has the [IPv4 add-on](/docs/guides/platform/ipv4-address).
### Pooler session mode
The session mode connection string connects to your Postgres instance through the shared pooler. Use it as an alternative to a direct connection when you connect from an IPv4-only network.
The connection string looks like this:
```txt
postgresql://postgres.[PROJECT-REF]:[YOUR-PASSWORD]@[POOLER-HOST]:5432/postgres
```
Get your project's session mode connection string from the Supabase Dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=session) and choosing **Session pooler**.
### Pooler transaction mode
The transaction mode connection string connects to your Postgres instance through the shared pooler in transaction-pooling mode. Use it for serverless and edge functions, which open many short-lived connections.
<Admonition type="caution">
Transaction mode does not support [prepared statements](https://postgresql.org/docs/current/sql-prepare.html). To avoid errors, [turn off prepared statements](https://github.com/orgs/supabase/discussions/28239) for your connection library.
</Admonition>
The connection string looks like this:
```txt
postgresql://postgres.[PROJECT-REF]:[YOUR-PASSWORD]@[POOLER-HOST]:6543/postgres
```
Get your project's transaction mode connection string from the Supabase Dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=transaction) and choosing **Transaction pooler**.
## Dedicated pooler
On paid plans, Supabase provisions a dedicated pooler, [PgBouncer](https://www.pgbouncer.org/), that runs alongside your Postgres database. The dedicated pooler runs in transaction mode only. For session mode, use the [shared pooler](#pooler-session-mode). It is reachable over IPv6, or over IPv4 if the project has the [IPv4 add-on](/docs/guides/platform/ipv4-address).
The connection string looks like this:
```txt
postgresql://postgres:[YOUR-PASSWORD]@db.[PROJECT-REF].supabase.co:6543/postgres
```
The dedicated pooler runs on the same machine as your database, so it connects with lower latency than the shared pooler. It also uses more of your project's compute resources. If your network supports IPv6, or you have the IPv4 add-on, use the dedicated pooler instead of the shared pooler.
Get your project's dedicated pooler connection string from the Supabase Dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=transaction).
## More about connection pooling
Connection pooling improves database performance by reusing existing connections between queries. This reduces the overhead of establishing connections and improves scalability.
You can use an application-side pooler or a server-side pooler, depending on whether your backend is persistent or serverless. Supabase provides a server-side pooler called Supavisor.
### Application-side poolers
Application-side poolers are built into connection libraries and API servers, such as Prisma, SQLAlchemy, and PostgREST. They maintain several active connections with Postgres or a server-side pooler, which reduces the overhead of establishing connections between queries. When you deploy to a persistent backend, such as a long-running container or VM, an application-side pooler is enough on its own.
### Server-side poolers
Postgres connections work like a WebSocket. Once established, a connection is preserved until the client disconnects. A server might make a single 10 ms query but hold its database connection for seconds or longer.
Server-side poolers, such as Supabase's [Supavisor](https://github.com/supabase/supavisor) in transaction mode, sit between clients and the database. Think of them as load balancers for Postgres connections.
<Image
alt="A direct connection reserves one database connection per client. A connection pooler shares a smaller set of database connections across many clients."
src={{
dark: '/docs/img/guides/database/connecting-to-postgres/how-connection-pooling-works.png',
light:
'/docs/img/guides/database/connecting-to-postgres/how-connection-pooling-works--light.png',
}}
width={1851}
height={907}
caption="Connecting to the database directly compared with using a connection pooler"
/>
Server-side poolers maintain hot connections with the database and share them with clients only when needed, which maximizes the number of queries a single connection can serve. Use them for queries from auto-scaling systems, such as edge and serverless functions.
## Connecting with SSL
Connect to your database using SSL wherever possible, to prevent snooping and man-in-the-middle attacks.
Download your server root certificate from [Database settings](/dashboard/project/_/database/settings) in the Supabase Dashboard. The same section has a toggle that rejects non-SSL connections to your database.
![The SSL Configuration section of Database settings, with a toggle to enforce SSL on incoming connections and a Download Certificate button.](/docs/img/database/database-settings-ssl.png)
## Resources
- [Connection management](/docs/guides/database/connection-management)
- [Connecting with psql](/docs/guides/database/psql)
- [Importing data into Supabase](/docs/guides/database/import-data)
## Troubleshooting and Postgres connection string FAQs
The following answers cover common connection problems and questions.
### What is a `connection refused` error?
A `connection refused` error means your database isn't reachable. Check that your Supabase project is running, confirm your database's connection string, check your firewall settings, and validate your network permissions.
### What is the `FATAL: Password authentication failed` error?
This error means your credentials are incorrect. Check your username and password in the Supabase Dashboard. If the problem persists, reset your database password in the project settings.
### How do you connect using IPv4?
You have two options. The shared pooler is IPv4-only on every plan, in both session and transaction mode. Alternatively, add the [IPv4 add-on](/docs/guides/platform/ipv4-address) to your project, which makes the direct connection and the dedicated pooler reachable over IPv4 instead of IPv6.
### Where is the Postgres connection string in Supabase?
Your connection string is in the Supabase Dashboard. Click [Connect](/dashboard/project/_?showConnect=true) at the top of the page.
### Can you use Supavisor and PgBouncer together?
You can use both, but don't do it unless you're trying to increase the total number of concurrent client connections. In most cases, choose either PgBouncer or Supavisor for pooled or transaction-based traffic. Direct connections remain the best choice for long-lived sessions, and shared pooler session mode is the alternative when those sessions need IPv4. Running both poolers at once increases the risk of hitting your database's maximum connection limit on smaller compute sizes.
### How does the default pool size work?
Supavisor and PgBouncer work independently, but both reference the same pool size setting. You can adjust it in [Database settings](/dashboard/project/_/database/settings) in the Supabase Dashboard.
Say you set the pool size to 30. Supavisor can then open up to 30 server-side connections to Postgres. Those 30 are shared between the session mode port, `5432`, and the transaction mode port, `6543`. Each mode can use all 30 on its own, or the two can split them, but the total across both modes cannot exceed 30.
PgBouncer can open up to 30 connections under the same limit. If both poolers reach their limits at the same time, you could have as many as 60 backend connections hitting your database, in addition to any direct connections.
### What is the difference between client connections and backend connections?
There are two limits to understand when working with poolers.
| Limit | What it counts | What sets it |
| ------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Client connections | How many clients can connect to a pooler at the same time | Your [compute size's max pooler clients limit](/docs/guides/platform/compute-and-disk#postgres-replication-slots-wal-senders-and-connections) |
| Backend connections | How many active connections a pooler opens to Postgres | The pool size for that pooler |
Both limits apply independently to Supavisor and PgBouncer.
```txt
Total backend load on Postgres =
Direct connections +
Supavisor backend connections (≤ supavisor_pool_size) +
PgBouncer backend connections (≤ pgbouncer_pool_size)
≤ Postgres max connections for your compute instance
```
### What is the max pooler clients limit?
The max pooler clients limit for your compute size applies separately to Supavisor and PgBouncer. One pooler reaching its client limit doesn't affect the other. When a pooler reaches this limit, it stops accepting new client connections until existing ones close. The other pooler is unaffected. You can check your connection limits in the [compute and disk documentation](/docs/guides/platform/compute-and-disk#postgres-replication-slots-wal-senders-and-connections).
### Where can you see current connection usage?
You can track connection usage in the [Observability](/dashboard/project/_/observability/database) section of the Supabase Dashboard. There are three reports:
- **Database Connections:** total active connections by role, including direct and pooled connections.
- **Dedicated Pooler Client Connections:** active client connections to PgBouncer.
- **Shared Pooler (Supavisor) Client Connections:** active client connections to Supavisor.
These reports are not real-time. They show the connection count from the last refresh. For up-to-the-second data, set up Grafana or query `pg_stat_activity` directly in the SQL Editor. The following queries report on current connections.
```sql
-- Count connections by application and user name
select
count(usename),
count(application_name),
application_name,
usename
from
pg_stat_ssl
join pg_stat_activity on pg_stat_ssl.pid = pg_stat_activity.pid
group by usename, application_name;
```
```sql
-- View all connections
select
pg_stat_activity.pid,
ssl as ssl_connection,
datname as database,
usename as connected_role,
application_name,
client_addr,
query,
query_start,
state,
backend_start
from pg_stat_ssl
join pg_stat_activity on pg_stat_ssl.pid = pg_stat_activity.pid;
```
### Why are there active connections when the app is idle?
Even when your application isn't making queries, some Supabase services keep persistent connections to your database. Storage, PostgREST, and the health checker all maintain long-lived connections. You usually see a small baseline of active connections from these services.
### Why do connection strings have different ports?
Different modes use different ports:
- Direct connection: `5432`, for Postgres on your project instance
- Dedicated pooler, transaction mode: `6543`, for PgBouncer on your project instance
- Shared pooler, transaction mode: `6543`, for Supavisor
- Shared pooler, session mode: `5432`, for Supavisor
The port routes the connection to the right pooler and mode.
### Does connection pooling affect latency?
The dedicated pooler runs on the same machine as your database, so it connects with lower latency than the shared pooler, which runs on a separate server. Direct connections have no pooler overhead, but they require IPv6 unless you have the IPv4 add-on.
### How to choose the right connection method?
**Direct connection:**
- Best for persistent backends
- Use for migrations, `pg_dump`, and backup and management tools
- Reachable over IPv6, or over IPv4 if the project has the [IPv4 add-on](/docs/guides/platform/ipv4-address)
**Shared pooler, Supavisor:**
- Best for connections from IPv4 networks. It is IPv4-only on every plan
- Session mode for a persistent backend on an IPv4 network
- Transaction mode for serverless functions and other short-lived tasks
- Use for application runtime traffic, such as queries and writes
**Dedicated pooler, PgBouncer, on paid plans:**
- Best for high-performance apps that need dedicated resources
- Use for application runtime traffic, such as queries and writes
- Transaction mode only. Use the shared pooler if you need session mode
- Reachable over IPv6, or over IPv4 if the project has the [IPv4 add-on](/docs/guides/platform/ipv4-address)
See the [table of connection modes](#how-to-connect-to-your-postgres-databases) at the top of this page for a quick reference, or follow the decision flow in the diagram below to choose the right option for your environment.
```mermaid
flowchart TD
A[Where are you connecting from?] --> B[Persistent Backend]
A --> C[Serverless / Edge]
B --> D{IPv6 Supported?<br/>IPv4 Add-on?}
B --> E{IPv4 Needed?}
C --> H{IPv6 Supported?<br/>IPv4 Add-on?}
C --> I{IPv4 Needed?}
D --> F[Use Direct Connection]
E --> G[Use Supavisor Session Mode]
H --> J[Use Dedicated Pooler PgBouncer Pro]
I --> K[Use Supavisor Transaction Mode]
```
The decision depends on where your code runs. For a persistent backend, use a direct connection if you can reach the database over IPv6 or have the IPv4 add-on. Otherwise, use the shared pooler in session mode. For serverless and edge environments, use the dedicated pooler on paid plans when IPv6 or the IPv4 add-on is available, or the shared pooler in transaction mode when you need IPv4.