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.
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 alreadyOr its close relative, when only the reserved slots are left:
FATAL: remaining connection slots are reserved for roles with the SUPERUSER attributePostgreSQL 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.
- App pools × instanceseach holds connections, mostly idlefill(fails)
- max_connections100 by default: full(breaks here)refuses
- A new connectionsorry, too many clients already
TL;DR
SHOW max_connections;
SELECT state, usename, application_name, client_addr, count(*)FROM pg_stat_activityWHERE backend_type = 'client backend'GROUP BY 1, 2, 3, 4ORDER 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.
sudo -u postgres psqlIn a container:
docker exec -it db psql -U postgresIf 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_queryFROM pg_stat_activityWHERE backend_type = 'client backend'ORDER BY state_changeLIMIT 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_activityWHERE 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 connectionsThat’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
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.1listen_port = 6432auth_type = scram-sha-256auth_file = /etc/pgbouncer/userlist.txtpool_mode = transactionmax_client_conn = 1000default_pool_size = 20The 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:
It only applies after a restart, not a reload:
sudo systemctl restart postgresqlWith 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.