Skip to content
All posts

Sep 2, 2026 · 1 min read

Serverless and the pooled connection

A database URL that works perfectly on one laptop can exhaust a Postgres server under real traffic. Why Neon gives you two endpoints, and which one goes where.

The connection string that ran this project for its first two days was the wrong one for production, and nothing would have said so until traffic arrived.

Two endpoints

Neon exposes every database on two hosts:

  • Directep-xxx.region.aws.neon.tech. A real Postgres connection.
  • Pooledep-xxx-pooler.region.aws.neon.tech. The same database behind PgBouncer, which multiplexes many short client connections over a few real ones.

On a laptop the difference is invisible: one process, one long-lived connection, direct is fine.

Why serverless changes the answer

On Vercel every request may run in a fresh function instance, and instances come and go constantly. Each one opens its own connection. Under load that is hundreds of connections against a server sized for a handful — and Postgres starts refusing them.

The pooled endpoint is built for exactly this: the function connects to PgBouncer, which hands it one of a small number of real connections for the duration of a transaction.

Why migrations cannot use it

PgBouncer in transaction mode does not support the session-level statements a migration needs (advisory locks held across statements, SET commands, prepared statements). So the CLI must use the direct endpoint.

That is why the project has two variables:

DATABASE_URL         # pooled — what the app uses at runtime
DIRECT_DATABASE_URL  # direct — what `prisma migrate deploy` uses

and why prisma.config.ts prefers the second for the CLI and falls back to the first locally, where a single unpooled connection is exactly right.

The general shape

This is the same lesson as the admin lock post, from the other direction: the configuration that is correct on one machine is not automatically correct at scale, and the only way to find out is to run it where scale exists. Deploy early, and treat the first production run as a test rather than a launch.

Comments

Nothing yet. Be the first.

    Sign in to comment.