Skip to content
ohsudo, Home
Databases

PostgreSQL "FATAL: sorry, too many clients already": find who holds the connections

Fix PostgreSQL "FATAL: sorry, too many clients already": see who holds the connections, end the idle ones, size your pools, and add PgBouncer before raising the limit.

Intermediate4 min readApplies to PostgreSQL 13+ · PgBouncer · Linux · Docker

by OhSudo ··

Checked on real software: PostgreSQL 18 · November 2026How we check

Not sure this is your cause?Find it step by step: Can't connect to the database →

Part of a learning path:SQL and PostgreSQL, step by step

FATAL: sorry, too many clients already

Or its close relative, when only the reserved slots are left:

FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute

PostgreSQL has reached max_connections, 100 by default, and is turning new connections away. Raising the number is the first idea, and usually the wrong one. Most of those connections are probably doing nothing.

  1. App pools × instanceseach holds connections, mostly idle
  2. max_connections100 by default: full(breaks here)
  3. A new connectionsorry, too many clients already
PostgreSQL turns new connections away at max_connections (100 by default). Most of the open ones are usually idle, held by connection pools.

TL;DR

SHOW max_connections;
SELECT state, usename, application_name, client_addr, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY 1, 2, 3, 4
ORDER BY count(*) DESC;

Lots of idle? Your application’s pools are too big, or it leaks connections. Lots of idle in transaction? Transactions aren’t being closed. Fix that, and put PgBouncer in front if you have many clients.

1. Get in

If you can’t connect at all, try as a superuser: PostgreSQL keeps superuser_reserved_connections slots for that, 3 by default.

Terminal window
sudo -u postgres psql

In a container:

Terminal window
docker exec -it db psql -U postgres

If those slots are gone too, usually because the application connects as a superuser, there’s no way in with SQL. Stop the application, or restart PostgreSQL as a last resort. And then stop using a superuser for the application.

2. See who holds them

The query in the TL;DR groups them. For the details of the oldest:

SELECT pid, usename, application_name, client_addr, state,
now() - state_change AS in_state_for,
left(query, 60) AS last_query
FROM pg_stat_activity
WHERE backend_type = 'client backend'
ORDER BY state_change
LIMIT 20;

In state:

State Means Usually
active Running a query right now If there are many, it’s a real load or slow queries
idle Connected, doing nothing Pools too large, or connections left open
idle in transaction Opened a transaction and went quiet A bug. It also blocks vacuum and can hold locks

3. Free some now

End a specific connection, or everything idle for over 10 minutes from one application:

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE usename = 'app'
AND state = 'idle'
AND state_change < now() - interval '10 minutes';

That’s safe for idle sessions, as a pool will reconnect. For idle in transaction, the open transaction rolls back. This buys you time, and only the next sections fix it.

4. Do the arithmetic on your pools

It’s the most common cause:

4 pods × 3 worker processes × 10 connections in each pool = 120 connections

That’s already over 100, before any traffic and before a deploy briefly doubles the pods. Add them up across every service using the database, including cron jobs and migrations.

Then set the pool sizes to fit. Most applications do fine with far fewer connections than they ask for. And check that connections go back: a request that exits early without closing leaks one each time.

5. Add limits so one client can’t take everything

ALTER ROLE app CONNECTION LIMIT 50;
ALTER ROLE reporting CONNECTION LIMIT 5;

And let the server clean up after buggy clients:

ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
ALTER SYSTEM SET idle_session_timeout = '30min';
SELECT pg_reload_conf();

idle_session_timeout is available since PostgreSQL 14. Set it carefully: a pool that doesn’t handle dropped connections will show errors, and you shouldn’t use it through PgBouncer.

6. Use a connection pooler

Each PostgreSQL connection is a separate process, so thousands of them cost memory and slow everything. PgBouncer sits in front, accepts thousands of clients and uses just a few real connections:

[databases]
shop = host=127.0.0.1 port=5432 dbname=shop
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20

The application then connects to port 6432 instead of 5432. In transaction mode a client only holds a real connection during a transaction, which is what makes it so effective. The catch is that session features such as SET, advisory locks and LISTEN don’t carry across transactions, so check what your application uses.

Serverless functions and many short-lived clients are where this helps the most: they can exhaust the limit even at low traffic. Managed services often have their own pooler, such as RDS Proxy.

7. Raising the limit, if you need to

After all of that, you may still need more:

ALTER SYSTEM SET max_connections = 200;

It only applies after a restart, not a reload:

Terminal window
sudo systemctl restart postgresql

With Docker, it’s command: postgres -c max_connections=200.

Warning

A higher limit isn’t free. Each connection takes memory, and each can use up to work_mem per sort or hash in its query, several times over. If 500 connections really run queries at once, the machine will swap or the OOM killer will end PostgreSQL, which is far worse than a refused connection.

Shell, we have a problem!

Similar, but different: could not connect to server: Connection refused means PostgreSQL isn’t reachable at all: see PostgreSQL connection refused. too many connections for role "app" is the CONNECTION LIMIT from section 5, and too many connections for database is the same limit on a database. On MySQL, the same problem looks like error 1040. For everyday admin commands, see the PostgreSQL Admin Cheat Sheet.

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Articles

Related articles