# How to connect to Postgres from serverless functions without running out of connections

Last updated: 2026-11-10

Put a connection pooler between your functions and Postgres, connect through it in transaction mode, and keep each function's own pool tiny, often a single connection created outside the handler. Many managed Postgres services include one: **Supabase**, **Neon** and **Aiven** run PgBouncer or a similar pooler behind a separate connection string, **Cloudflare** Workers get Hyperdrive, and **AWS Lambda** functions can reach Amazon RDS through RDS Proxy. If your runtime can't open TCP connections at all, switch to an HTTP-based driver or data API instead.

## Why functions use up connections

Postgres caps concurrent connections with `max_connections`, and its docs say the default is typically 100. By default 3 of those slots are kept for superusers. Hosted plans size the cap to the compute you pay for: on Neon, a 0.25 CU compute with 1 GB of RAM allows 112 connections, 7 of which Neon keeps for itself.

Serverless platforms multiply clients. AWS Lambda gives each concurrent request its own execution environment, and each environment keeps its own copy of your database client. A traffic spike that runs 200 copies of a function, each with a pool of 10, asks for 2,000 connections. Once the cap is reached, Postgres refuses new connections and those requests fail.

## Poolers at a glance

| Platform | Pooler | Pooling mode | Worth knowing |
|---|---|---|---|
| Supabase | Supavisor shared pooler; dedicated PgBouncer on Pro and above | Transaction on port 6543, session on port 5432 | The shared pooler doesn't support prepared statements in transaction mode |
| Neon | PgBouncer, reached by adding `-pooler` to the host name | Transaction | Accepts up to 10,000 client connections; settings aren't user-configurable |
| Aiven | PgBouncer | Session, transaction or statement | Needs a Startup plan or higher |
| Scaleway | Built into Serverless SQL Database | Not documented | Some Postgres features behave differently behind it |
| Cloudflare | Hyperdrive, for Workers | Transaction | Keeps the pool in regions close to your database |
| AWS RDS Proxy | Managed proxy for Amazon RDS | Not stated | Queues or throttles connections it can't serve right away |
| Self-hosted PgBouncer | Open-source pooler you run | Session, transaction or statement | Defaults: 100 clients, 20 server connections per user and database |
| Eurobase | None; it hosts the app, not Postgres | Not offered | Container apps can use any pooler above |

Details are from each provider's own documentation, checked on 9 and 10 November 2026.

## What to check before you pick a setup

- **Transaction or session mode.** Session mode holds one server connection for as long as the client stays connected, which barely helps functions. Transaction mode hands the connection back after each transaction, so many clients share a few server connections.
- **Session features your code relies on.** PgBouncer's feature table lists what transaction mode can't do: `SET` and `RESET`, `LISTEN`, SQL-level `PREPARE`, `WITH HOLD` cursors, temporary tables that keep rows, and session advisory locks. A `SET search_path` run once when the client connects, for example, doesn't follow it to the next server connection.
- **Prepared statements.** Some drivers and ORMs prepare statements at the protocol level. PgBouncer handles those in transaction mode only when `max_prepared_statements` is above zero, and not every hosted pooler does. If yours doesn't, turn prepared statements off in the driver.
- **Whether your runtime can open TCP.** Isolate runtimes often can't. There you need an HTTP driver, a data API, or a pooler that the platform exposes as a binding.
- **Where the pooler sits.** A pooler next to the database saves round trips on every new connection, and one that's far away adds latency to each query.
- **Migrations.** Keep a second, direct connection string for schema changes and other admin commands. Prisma, for example, reads a separate `DIRECT_URL` for its CLI.

## How each approach works

### A pooler in front of Postgres

PgBouncer is what Neon, Aiven and Supabase's dedicated pooler run. Your functions connect to it as if it were Postgres, and it keeps a small set of real server connections. Its docs describe three modes: session, transaction, and statement, which also forbids transactions spanning several statements.

Neon shows what the numbers look like in practice. Its pooler takes up to 10,000 client connections, but on a 1 CU compute only about 405 transactions per user and database run at once, and clients beyond that wait in a queue for up to 2 minutes. A pooler absorbs bursts; it doesn't make the database faster.

### The provider's own pooled string

On Supabase, open Connect and choose the transaction pooler for serverless and edge functions, which is what its docs recommend for code that opens many short-lived connections. On Neon, copy the pooled string with the `-pooler` host. Aiven lets you create pools per database and user and pick the mode for each one.

### A platform proxy

Cloudflare's Hyperdrive sits between Workers and your database: it sets up connections near the Worker, pools them near the database, and can cache query results. Its docs tell you to create a new client on every request, because Hyperdrive keeps the real pool. On AWS, RDS Proxy pools connections in front of Amazon RDS, can require IAM authentication from clients, and connects to a standby instance during a failover while keeping application connections open.

### HTTP drivers and data APIs

Neon's serverless driver sends each query as an HTTP request and switches to WebSockets only when you need interactive transactions or a node-postgres compatible pool. Supabase's Data API answers REST and GraphQL requests, so no Postgres connection is involved. Both suit runtimes without TCP. With a data API, work that needs several statements in one transaction is easier to put in a Postgres function you call.

## Driver settings that keep the count down

1. Create the client or pool outside the handler. AWS and Prisma both recommend it, so a warm copy of the function reuses its connection instead of opening a new one.
2. Keep the pool small. node-postgres defaults to `max: 10` per pool. A Lambda environment handles one request at a time, so behind a pooler a pool of 1 covers it. Prisma's serverless advice is likewise to configure a small pool.
3. Don't close the connection at the end of every request. Prisma's docs say not to call `$disconnect()` in serverless handlers.
4. Let idle clients go. node-postgres drops a client after 10 seconds idle by default through `idleTimeoutMillis`, and `maxLifetimeSeconds` rotates long-lived ones.
5. Cap concurrency where you can. Lambda's reserved concurrency puts a ceiling on how many copies can hit the database at once.

## Where Eurobase fits

Eurobase doesn't host Postgres, so it isn't a pooler option. It hosts the app that connects to one, on European infrastructure run by Scaleway, a French provider. How that app connects depends on the runtime.

A container app is a long-running server, so an ordinary driver with a normal in-process pool works: Prisma, Drizzle, pg or psycopg. Each running instance keeps its own pool, so point them at a pooled connection string from one of the providers in [managed Postgres in Europe](/i/managed-postgres-europe) or [European alternatives to Neon](/i/neon-alternatives-europe). The [Eurobase + Supabase guide](/docs/supabase) documents the Supabase transaction pooler on port 6543 with prepared statements turned off. The Supabase direct host has only an IPv6 address, which a Eurobase container can't reach.

Apps on V8 isolates can't open outbound TCP connections, so Postgres drivers don't connect from them. Use supabase-js or another HTTPS data API there, or the D1-compatible SQLite databases built into the project, covered in [serverless SQLite in Europe](/i/serverless-sqlite-europe).

- **Best for:** running a Next.js, Node.js or Python app in Europe that talks to a managed Postgres from another provider.
- **Price:** a Free plan, then Starter at €10 or Pro at €20 a month + VAT per workspace. There are no usage charges today: work over a limit is refused, apps on V8 isolates and Functions stop until the monthly reset, and containers and static sites keep serving.
- **What it lacks:** managed Postgres, a built-in pooler, and TCP from isolates. ISO 27001 and SOC 2 are being worked on, not yet held.

## Frequently asked questions

### Should I use transaction mode or session mode for serverless?

Transaction mode. It returns the server connection to the pool after every transaction, which is what lets hundreds of short-lived functions share a few dozen real connections. Session mode suits long-running servers and code that relies on `SET`, `LISTEN` or session advisory locks.

### Why do I get "prepared statement already exists" errors behind a pooler?

In transaction mode, a statement prepared on one server connection may not exist on the next one your client gets. Either use a pooler that tracks protocol-level prepared statements, such as PgBouncer with `max_prepared_statements` set, or turn prepared statements off in your driver or ORM.

### Do I still need a pool in my code if I use PgBouncer?

Yes, but a small one. Keep one client per function instance, created outside the handler, so warm invocations reuse it. The external pooler handles sharing across instances.

### Can edge functions connect to Postgres directly?

It depends on the runtime. Eurobase's V8 isolates don't allow outbound TCP, so Postgres drivers can't connect from them. Cloudflare Workers connect through Hyperdrive; elsewhere use an HTTP driver such as Neon's or a data API such as Supabase's.

### How many connections can my Postgres handle?

Check `max_connections` on your plan. The Postgres default is typically 100, and hosted plans size it by memory. Neon, for example, allows 112 on a 0.25 CU compute with 1 GB of RAM and 450 on a 1 CU compute with 4 GB.

## Sources

- [PostgreSQL connection settings](https://www.postgresql.org/docs/current/runtime-config-connection.html)
- [PgBouncer features](https://www.pgbouncer.org/features.html) and [PgBouncer configuration](https://www.pgbouncer.org/config.html)
- [Supabase: connect to your database](https://supabase.com/docs/guides/database/connecting-to-postgres)
- [Neon connection pooling](https://neon.com/docs/connect/connection-pooling) and [Neon serverless driver](https://neon.com/docs/serverless/serverless-driver)
- [Aiven connection pooling with PgBouncer](https://aiven.io/docs/products/postgresql/concepts/pg-connection-pooling) and [Aiven: manage connection pools](https://aiven.io/docs/products/postgresql/howto/manage-pool)
- [How Hyperdrive works](https://developers.cloudflare.com/hyperdrive/concepts/how-hyperdrive-works/) and [Hyperdrive overview](https://developers.cloudflare.com/hyperdrive/)
- [Amazon RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)
- [AWS Lambda best practices](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html) and [Lambda concurrency](https://docs.aws.amazon.com/lambda/latest/dg/lambda-concurrency.html)
- [node-postgres Pool](https://node-postgres.com/apis/pool)
- [Prisma: databases connections](https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections)
- [Scaleway Serverless SQL Database FAQ](https://www.scaleway.com/en/docs/serverless-sql-databases/faq/)
- [Eurobase + Supabase](/docs/supabase), [Eurobase Workers](/docs/workers), [Eurobase containers](/docs/containers), [Eurobase plans](/docs/plans) and [European Supabase alternatives](/i/european-supabase-alternatives)
