Database
Connection pool
Size the PostgreSQL connection pool of a VitNode app for a long-running server, serverless hosting or a transaction pooler such as PgBouncer or the Supabase pooler.
VitNode talks to Postgres through postgres.js, and the default config hands it a bare connection string:
dbProvider: drizzle({
connection: POSTGRES_URL,
relations: coreRelations,
}),That means postgres.js defaults: up to 10 connections per process, idle connections kept forever (idle_timeout: 0), and automatic prepared statements (prepare: true). Perfectly fine on your laptop. In production you probably want to pick the numbers yourself.
Swap the string for an object with url plus any postgres.js option:
Long-running server
A Node server, a Docker container, a VPS. One process lives for days and handles plenty of requests at once, so give it room:
dbProvider: drizzle({
connection: POSTGRES_URL,
connection: { url: POSTGRES_URL, max: 20, idle_timeout: 20 },
relations: coreRelations,
}),| Option | Default | What it does |
|---|---|---|
max | 10 | The most connections this process will hold open. Queries beyond it wait their turn. |
idle_timeout | 0 | Seconds before an unused connection is closed. 0 never closes it; 20 frees quiet periods. |
prepare | true | Creates named prepared statements automatically. Turn it off behind a transaction pooler. |
Sizing rule of thumb: every process opens its own pool, and Postgres has a hard ceiling (SHOW max_connections;, often 100). So:
max ā (max_connections ā headroom) Ć· number of processesKeep 10-20 connections of headroom for migrations, backups and you poking around in psql. With max_connections = 100, 4 instances and 20 spare, that is max: 20 each. Scale out to 8 instances and the same math says max: 10. Forget to redo it and the fifth instance greets you with too many connections.
Serverless
On Vercel, Netlify and friends every concurrent request may be its own process, so max Ć processes grows faster than you would like. Use your provider's pooled connection string, or keep each process to a single connection with max: 1. The Vercel guide has the full setup.
Behind a transaction pooler
The Supabase pooler on port 6543, PgBouncer with pool_mode = transaction
and similar poolers hand you a different backend connection for every
transaction. A prepared statement created on one backend does not exist on
the next, so queries fail at random with prepared statement "..." does not exist. Turn automatic prepared statements off:
dbProvider: drizzle({
connection: { url: POSTGRES_URL, prepare: false },
relations: coreRelations,
}),Direct connections and session-mode poolers keep prepare: true, the faster option. VitNode's own locks are transaction-scoped (pg_advisory_xact_lock), so they work behind either mode.