Fixing "FATAL: sorry, too many clients already" in PostgreSQL

Posted by Kyle Hankinson


Your application cannot reach the database, and the logs show one of these:

FATAL:  sorry, too many clients already
FATAL:  remaining connection slots are reserved for roles with the SUPERUSER attribute

Both mean the same underlying thing: every connection slot up to max_connections is taken, and Postgres is turning new clients away at the door. The second wording appears a little earlier because Postgres holds back a few slots (superuser_reserved_connections, default 3) so an administrator can still get in while ordinary users cannot. That exact message is what PostgreSQL 16 prints; on PostgreSQL 14 the same condition reads remaining connection slots are reserved for non-replication superuser connections. I reproduced both on Docker instances started with max_connections=5: two app-role connections plus the reserve filled the server, the third app connection got the "reserved" message, and once superuser sessions consumed the reserve too, the next attempt got "sorry, too many clients already".

The instinctive response is to raise max_connections. Sometimes that is right, but in most incidents the slots are being wasted rather than used, so start by finding out who is holding them.

Step 1: See how bad it is

Connect as a superuser (this is what the reserved slots are for) and run:

SHOW max_connections;

SELECT count(*) FROM pg_stat_activity
WHERE backend_type = 'client backend';

The backend_type filter matters: pg_stat_activity also lists internal workers like the autovacuum launcher and WAL writer, and you only care about client sessions. If the count is at or near max_connections, you are in the right article.

Step 2: Find out who is hoarding slots

Group the sessions by who owns them and what they are doing:

SELECT usename, application_name, state, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY usename, application_name, state
ORDER BY count(*) DESC;

The state column is the diagnostic gold. active sessions are running queries; a wall of idle sessions from one application usually means an oversized or leaking connection pool; idle in transaction sessions are the worst case, holding a transaction (and its locks) open while doing nothing. All of these columns are stable from PostgreSQL 10 onward; the full column list is in the monitoring documentation.

To see how long the squatters have been squatting:

SELECT pid, usename, state,
       now() - state_change AS in_state_for,
       left(query, 45) AS last_query
FROM pg_stat_activity
WHERE state IN ('idle', 'idle in transaction')
ORDER BY state_change
LIMIT 10;

On a test server where I parked a session mid-transaction, this surfaced it immediately: idle in transaction for 22 seconds with the last completed statement still attached. In production you will see the same shape with hours instead of seconds. These queries are plain SQL, so they run as-is from any client; in SQLPro Studio you can export the result grid to CSV or JSON, which is handy for keeping a snapshot of exactly who was connected when the incident hit.

Step 3: Reclaim slots to stop the bleeding

You can terminate sessions server-side:

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND now() - state_change > interval '10 minutes';

Verified on PostgreSQL 16: the function returns t per session and the backend disappears from pg_stat_activity. Be careful with this. pg_terminate_backend kills the session and rolls back its open transaction; whatever that application had half-done is gone, and a client that does not handle disconnects will surface errors to its users. Target it narrowly (specific PIDs, long idle times, known-leaky app names) rather than sweeping everything. For a single stuck query, prefer pg_cancel_backend, which cancels the statement but keeps the session; the difference is covered in killing or cancelling a long-running Postgres query. The same technique clears connections ahead of maintenance, as in dropping a database with active connections.

Step 4: Keep it from happening again

Set a timeout for abandoned transactions. This is the single highest-value setting in this area:

ALTER SYSTEM SET idle_in_transaction_session_timeout = '10min';
SELECT pg_reload_conf();

Caution: any session that sits idle inside a transaction longer than the limit is disconnected, so make sure nothing legitimate (interactive psql sessions during migrations, for example) needs to hold transactions open longer. PostgreSQL 14+ also offers idle_session_timeout for sessions idle outside a transaction, which is stricter medicine; both are described in the connection settings documentation.

Cap chatty roles. If one service must never starve the others:

ALTER ROLE app_user CONNECTION LIMIT 30;

Verified on PostgreSQL 16: connections beyond the limit fail with FATAL: too many connections for role "app_user", which pins the failure on the misbehaving service instead of taking down every consumer of the database.

Fix the pool, not the symptom. The most common root causes are boring: a pool created per request instead of per process, a serverless function that opens a raw connection per invocation, or twelve app instances each configured with a 20-connection pool against a 100-connection server. Postgres backends are processes, and each one costs real memory and coordination overhead, which is why max_connections = 1000 tends to make throughput worse, not better. The PostgreSQL wiki's Number Of Database Connections page makes the case that a small pool feeding queued requests usually outperforms a huge connection count. If you cannot shrink the client side, put PgBouncer in transaction mode in front of the server; thousands of client connections then share a few dozen real backends.

Raise max_connections only after the math says so. If every session is genuinely active and short-lived and you still hit the ceiling, then yes, raise it (it requires a server restart) and size memory accordingly, since settings like work_mem are per operation within each backend. On PostgreSQL 16 and later there is also reserved_connections, which holds back slots for roles granted pg_use_reserved_connections, so your monitoring or migration tooling can keep superuser-free guaranteed access.

One last note for managed Postgres: RDS, Supabase, Heroku, and similar services often cap connections well below the stock default of 100 on smaller plans, and some route you through a built-in pooler with its own limits. If the numbers from Step 1 look nothing like your configuration, check the plan's connection limit before blaming your code.


Tags: PostgreSQL

About the author -- Kyle Hankinson is the founder and sole developer of SQLPro for MSSQL and the Hankinsoft Development suite of database tools. He has been building native macOS and iOS applications since 2010.

Try SQLPro for MSSQL -- A native SQL Server client for macOS, iOS, and Windows. No virtual machines required.

Download Free Trial View Pricing Compare