"PgBouncer Pool Modes: Session, Transaction, Statement Explained"
"Pgbouncer pool modes explained for app backends — session vs transaction vs statement, the 6543/5432 split on Supabase, and per-driver prepared-statement fixes."
Your backend opens a database connection per request, the pool fills up at a few hundred concurrent users, and Postgres starts rejecting connections — "too many clients". The fix is a pooler like PgBouncer sitting between your app and Postgres, multiplexing thousands of client connections onto a handful of real server connections. But the mode you pick changes what your SQL is allowed to do. Here are pgbouncer pool modes explained for app backends: which mode to run, what breaks in transaction mode, and the exact per-driver fixes. Last updated: October 4, 2026 What you'll learn The three PgBouncer pool modes: session, transaction, statement Transaction pooling vs session pooling: what changes and when to pick each Which connection string to use on Supabase/Neon: port 6543 vs 5432 The prepared-statement problem in transaction mode, with fixes for asyncpg, psycopg3, Prisma, and Drizzle Transaction mode limitations (LISTEN, advisory locks, temp tables) Sizing basics: default_pool_size and max_client_conn PgBouncer Pool Modes Explained: Session, Transaction, Statement A pooler holds *server connections* (real Postgres backends) and hands them to *client connections* (your app). The mode decides when a server connection is returned to the pool : Session pooling — a server connection is assigned to a client for the entire session, until disconnect. Safest: everything works exactly like a direct connection, because it effectively is one. Least efficient: an idle client still holds its backend. Transaction pooling — a server connection is assigned per *transaction*. When your transaction commits or rolls back, the backend goes back to the pool and the next transaction from any client can reuse it. This is the high-throughput mode — one backend can serve hundreds of…