The Error
Your app suddenly fails with one of these:
FATAL: sorry, too many clients already
FATAL: remaining connection slots are reserved for non-replication superuser connections
FATAL: too many connections for role "app_user"Every PostgreSQL connection is a separate server process using memory. Postgres caps them with max_connections (100 by default). When your app opens more than that, new connections are refused.
The fix is rarely "raise the limit." It's usually connection pooling and fixing code that opens too many connections.
🎯 Quick Fix
- See who's connected (query below) and kill idle connections if you're in an emergency.
- Use a connection pooler: PgBouncer, Supabase's pooler, Neon's pooled connection string, or RDS Proxy.
- Create the database client once per process, not per request.
- In serverless, always use the pooled connection string and keep each function's pool size small.
Why It Happens
pool: 10"] F2["fn 2
pool: 10"] F3["fn ...50
pool: 10"] end F1 --> PG[("Postgres
max_connections = 100")] F2 --> PG F3 --> PG PG --> X["❌ 500 connections wanted,
100 allowed"]
Common causes:
- Serverless scaling: each function instance opens its own connections. 50 instances × 10 connections = 500.
- Creating a new client per request instead of reusing one.
- Connection leaks: connections opened and never released (missing
release()/finally). - Dev hot reload: frameworks like Next.js re-run modules on reload, creating new clients each time.
- Idle transactions: sessions stuck "idle in transaction" hold connections forever.
Step 1: See Who's Connected
SELECT usename, application_name, client_addr, state, count(*)
FROM pg_stat_activity
GROUP BY 1, 2, 3, 4
ORDER BY count(*) DESC;
SHOW max_connections;Look for a single app or IP holding many connections, or lots of idle and idle in transaction sessions.
Emergency cleanup (terminates idle sessions older than 10 minutes; be careful in production):
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND state_change < now() - interval '10 minutes'
AND pid <> pg_backend_pid();Step 2: Add a Connection Pooler
hundreds of client connections"] --> PB["Pooler
(PgBouncer, Supavisor,
RDS Proxy)"] PB -->|"~20 real connections"| PG[("Postgres")]
A pooler accepts many lightweight client connections and shares a small number of real Postgres connections between them.
| Platform | What to use |
|---|---|
| Self-hosted | PgBouncer in front of Postgres |
| Supabase | The pooler connection string (transaction mode for serverless) from your project's Connect settings |
| Neon | The pooled connection string (hostname contains -pooler) |
| AWS RDS / Aurora | RDS Proxy |
SET and advisory locks may not behave as expected. Many ORMs need a setting for this, such as Prisma's pgbouncer=true connection parameter.
Step 3: Fix Your Code
Reuse one pool per process (Node.js):
// db.js — created once, imported everywhere
import pg from "pg";
export const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: 5, // small per instance, especially in serverless
idleTimeoutMillis: 10_000,
});
// always release
const client = await pool.connect();
try {
await client.query("SELECT 1");
} finally {
client.release();
}Avoid duplicate clients during Next.js hot reload:
const globalForDb = globalThis as unknown as { pool?: pg.Pool };
export const pool = globalForDb.pool ?? new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 5 });
if (process.env.NODE_ENV !== "production") globalForDb.pool = pool;Kill stuck transactions automatically:
ALTER DATABASE mydb SET idle_in_transaction_session_timeout = '60s';Step 4: Only Then, Consider max_connections
Raising max_connections (in postgresql.conf, followed by a restart) can help, but each connection uses memory, and hundreds of active connections can make Postgres slower, not faster. Managed providers often tie the limit to instance size. Treat a higher limit as a last step, after pooling.
Checklist
- Checked
pg_stat_activityfor the biggest consumers - Using a pooler (PgBouncer, Supabase/Neon pooled URL, RDS Proxy)
- One client/pool per process, reused across requests
- Every connection released in a
finally - Small pool size per serverless instance
-
idle_in_transaction_session_timeoutset
Frequently Asked Questions
What is the default max_connections in PostgreSQL?
The default is 100, though managed providers often set a different value based on instance size. A few slots are reserved for superusers, which is why you may see "remaining connection slots are reserved" before reaching the full number.
Why do serverless apps run out of Postgres connections?
Each serverless function instance runs separately and opens its own connections. Under traffic, the platform starts many instances at once, multiplying connections far beyond the database's limit. A connection pooler and small per-instance pools solve this.
Should I just increase max_connections?
Usually not as a first step. Every Postgres connection is a process using memory, and too many active connections reduce performance. Pooling lets many clients share a small number of real connections, which scales much better.
Conclusion
"Too many connections" means your app opens more Postgres connections than the server allows. Find who's holding them, put a connection pooler in front of the database, reuse one pool per process, release every connection, and keep serverless pools small. Raise max_connections only after that.
Sources
- PostgreSQL docs: Connections and authentication (max_connections)
- PostgreSQL docs: pg_stat_activity
- PgBouncer documentation
- Supabase docs: Connecting to your database
Last verified: 2 November 2026.
Related reads: PostgreSQL vs MySQL · Prisma vs Drizzle · Serverless vs Containers · Fix Supabase RLS Policy Error
I write practical guides for developers. Follow on LinkedIn or X, or invite me to speak.