FATAL: sorry, too many clients already means the number of open backend connections has reached max_connections, so PostgreSQL refuses any new session until one frees up. A related variant, FATAL: remaining connection slots are reserved for non-replication superuser connections (worded as ... reserved for roles with the SUPERUSER attribute in newer releases), appears when only the slots held back by superuser_reserved_connections remain. Both are exhaustion errors: the server is at capacity, not misconfigured network-wise.
What This Error Means
Every PostgreSQL connection is backed by a dedicated operating-system process (a postgres backend), forked by the postmaster at connect time. The server caps the total at max_connections, which defaults to 100. Once that many backends exist, the postmaster rejects further connection attempts with sorry, too many clients already before authentication completes.
A slice of those slots is held in reserve. superuser_reserved_connections (default 3) keeps the last few slots for superusers so an administrator can still log in to investigate. PostgreSQL 16 added reserved_connections (default 0), which reserves slots for roles granted pg_use_reserved_connections. So a non-superuser actually starts failing once usage reaches max_connections - superuser_reserved_connections - reserved_connections, not at the raw max_connections value.
The cost of each connection is real: every backend reserves memory for catalog caches and per-process state, and a query can allocate work_mem per sort or hash operation, so a high max_connections consumes memory and CPU even when most sessions are idle. That is why raising the limit is rarely the right first move.
Common Causes
Identify which cause applies before changing anything - the fix differs sharply between a leak, an idle-transaction problem, and genuine load.
| # | Cause | How to confirm |
|---|---|---|
| 1 | No pooler; each request opens its own connection | pg_stat_activity count tracks request rate; many short-lived sessions |
| 2 | Application pools sized larger than max_connections (summed across app instances) |
Total of all pool max settings exceeds the server limit |
| 3 | Connection leak - sessions opened but never closed | Backend count climbs steadily after a deploy and never drops |
| 4 | Sessions stuck idle in transaction holding slots |
state = 'idle in transaction' rows with old state_change |
| 5 | Long-running queries pinning connections | state = 'active' with large now() - query_start |
| 6 | Genuine concurrency exceeding capacity | Counts near max_connections correlate with real traffic peaks |
| 7 | Per-database limit hit (datconnlimit) |
pg_database.datconnlimit is a positive number below server capacity |
How to Fix This Error
First, see what is using the slots. Run this diagnostic to break down connections by state, database, and application:
-- Connections grouped by state, database, and client application
SELECT datname,
usename,
application_name,
state,
count(*) AS conns,
max(now() - state_change) AS max_in_state
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY datname, usename, application_name, state
ORDER BY conns DESC;
Check current usage against the configured limit:
-- Open client connections vs the configured ceiling
SELECT count(*) AS open_conns,
current_setting('max_connections')::int AS max_connections,
current_setting('superuser_reserved_connections')::int AS superuser_reserved
FROM pg_stat_activity
WHERE backend_type = 'client backend';
Free stuck sessions. Terminate connections sitting
idle in transactionfor too long, then fix the application code path that leaves transactions open:-- Terminate client backends idle in a transaction for over 10 minutes SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > interval '10 minutes';Set
idle_in_transaction_session_timeoutso PostgreSQL reclaims abandoned transactions automatically (it is0, disabled, by default):ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min'; SELECT pg_reload_conf();Put a connection pooler in front of PostgreSQL. This is the durable fix for causes 1, 2, and 3. PgBouncer in transaction-pooling mode multiplexes thousands of client connections onto a small set of server connections. RDS Proxy and pgpool-II serve the same role on their platforms. Size the pool so the sum of server-side connections stays well below
max_connections.Right-size application pools. Make the total maximum pool size across all application instances fit inside the server limit minus reserved slots. A leak (cause 3) shows as a pool that never returns connections - fix the close or
finallypath.Raise a too-low per-database limit if cause 7 applies.
ALTER DATABASE mydb CONNECTION LIMIT -1removes the per-database cap (datconnlimit), deferring to the server-widemax_connections.Raise
max_connectionsonly as a last resort, after confirming the load is genuine and the host has RAM to spare. It requires a restart:ALTER SYSTEM SET max_connections = 200; -- requires server restart
Root-Cause Analysis with Pulse
Telling apart a connection leak, a flood of idle in transaction sessions, and a legitimate traffic spike means sampling pg_stat_activity repeatedly during the incident, correlating it with deploy timing and per-application pool behavior - work that is hard to do after the slots are already exhausted and new sessions, including your own, are being refused. Pulse automates that loop. Its agentic SRE engine continuously tracks connection counts by state, database, and application, detects the climb toward max_connections, and traces it to the responsible source: a pool that stopped releasing connections after a deploy, transactions left open by a code path, or real concurrency outrunning capacity. It then recommends a targeted fix - terminate these sessions, cap that pool, add a transaction-mode pooler - for an operator to approve rather than guessing under pressure.
Preventive Measures
Connection exhaustion is predictable and therefore preventable. The controls below stop it from recurring.
- Standardize on a pooler. Route all application traffic through PgBouncer (transaction mode), pgpool-II, or RDS Proxy so the database never sees more sessions than it can hold. See connection pooling with PgBouncer and configuring a connection pool.
- Cap application pools to a known budget. Sum every instance's pool maximum and keep it under
max_connections - superuser_reserved_connections. - Keep
idle_in_transaction_session_timeoutset (for example5min) so a missedCOMMITcannot park a slot indefinitely. - Alert before the wall, not at it. Trigger at around 80% of effective capacity so there is time to react before sessions are refused.
Connecting your database to Pulse's proactive monitoring catches the slow drift - a creeping backend count after a deploy, a pool that quietly stopped recycling connections, idle-in-transaction sessions accumulating - well before usage reaches max_connections and the error surfaces to users.
Frequently Asked Questions
Q: What does "FATAL: sorry, too many clients already" mean in PostgreSQL?
A: It means the number of open backend connections has reached max_connections, so PostgreSQL refuses the new connection before authentication. Each connection is a separate server process, and the postmaster will not exceed the configured ceiling.
Q: Should I just increase max_connections to fix too many connections?
A: Usually no. Every connection consumes memory and CPU even when idle, so raising max_connections can push the host into swapping or out-of-memory conditions. A transaction-mode connection pooler such as PgBouncer fixes the problem at far lower resource cost; raise max_connections only when load is genuinely concurrent and RAM allows.
Q: What is the difference between superuser_reserved_connections and reserved_connections?
A: superuser_reserved_connections (default 3) holds slots so superusers can still connect when the server is full. reserved_connections (added in PostgreSQL 16, default 0) reserves slots for roles granted pg_use_reserved_connections. Non-superusers begin failing once usage hits max_connections minus both reserves.
Q: Why do idle connections still cause "too many clients already"?
A: An idle connection still occupies a slot and a backend process, so it counts against max_connections even though it runs no query. Sessions stuck idle in transaction are worse: they also hold locks and block vacuum. Set idle_in_transaction_session_timeout to reclaim them.
Q: How do I see which application is opening too many PostgreSQL connections?
A: Query pg_stat_activity grouped by application_name, usename, datname, and state. The application_name column, set by most client libraries, points to the service responsible, and state separates active queries from idle and idle-in-transaction sessions.
Q: Does PgBouncer transaction pooling solve too many connections?
A: Yes, for most web and API workloads. Transaction-mode pooling assigns a server connection only for the duration of a transaction, so thousands of client connections share a small server pool that stays under max_connections. The trade-off is that session-level features like server-side prepared statements and SET need care under transaction pooling.
Q: How do I terminate connections to free up slots immediately?
A: Use pg_terminate_backend(pid) against the offending PIDs from pg_stat_activity - typically those idle in transaction or running runaway queries. Because the last slots are reserved for superusers, an administrator can still connect to run the termination.
Related Reading
- PostgreSQL Connection Pooling with PgBouncer: Multiplex many clients onto few server connections to avoid exhaustion.
- Configuring a PostgreSQL Connection Pool: Size pools so the total stays under max_connections.
- PostgreSQL Connection Refused: The network-level connection failure, distinct from slot exhaustion.
- PostgreSQL: out of shared memory: Another capacity error tied to per-connection resource limits.
- PostgreSQL Performance Tuning: Memory and configuration levers that interact with connection count.
- Database Monitoring Best Practices: Which connection metrics to alert on before the limit is hit.