How to Fix Postgres 'Sorry, Too Many Clients Already' (2026)
This guide is for anyone whose application logs just filled up with FATAL: sorry, too many clients already or remaining connection slots are reserved, and who wants the outage over and the cause gone. By the end you will know who is holding the connections, have stopped the leak, have reserved slots so you can always log in, and have PgBouncer 1.26.1 in transaction mode in front of Postgres. Everything targets PostgreSQL 14 to 18 and PgBouncer 1.26.1 (released 14 October 2026); I reproduced the errors and ran the diagnostic queries on a scratch PostgreSQL 16.8 instance with max_connections = 8. You need psql access with a superuser or a role granted pg_signal_backend, and root on a host for the pooler. If you want the background on why connections are expensive and how the pool modes differ, read Postgres connection pooling explained first.
What the error means
Postgres has a fixed number of connection slots, set by max_connections (default 100). A few of them are held back, and which message you see depends on which part of the budget ran out. Here is what my scratch server printed with max_connections = 8, superuser_reserved_connections = 2 and reserved_connections = 2:
$ psql -U app postgres -c "select 1"
psql: error: connection to server at "127.0.0.1", port 55432 failed: FATAL: remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role
$ psql -U ops postgres -c "select 1" # ops is a member of pg_use_reserved_connections
psql: error: connection to server at "127.0.0.1", port 55432 failed: FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute
$ psql -U postgres -c "select 1" # every slot, reserved ones included, in use
psql: error: connection to server at "127.0.0.1", port 55432 failed: FATAL: sorry, too many clients already
So the ordinary error for an application role is the "reserved" one, not "too many clients": it means the non-reserved slots are gone. On Postgres 15 and older the same condition reads remaining connection slots are reserved for non-replication superuser connections (from the PG 15 source). "Too many clients" means even the superuser slots are full. All of them carry SQLSTATE 53300. Raising max_connections makes the error go away for a while; it almost never fixes the cause.
Step 1: Find who holds the connections with pg_stat_activity
If you cannot connect at all, you need a slot first. A superuser can use the superuser_reserved_connections slots (default 3) unless they are also full; on RDS the master user is not a true superuser and gets only the slots set by rds.rds_superuser_reserved_connections (AWS Knowledge Center), so if those are full you may have to stop one client or reboot. Once you are in, group the client backends:
-- Who is connected, from where, and in what state (PostgreSQL 10+)
SELECT usename,
application_name,
client_addr,
state,
count(*) AS conns,
max(now() - state_change) AS longest_in_state
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY 1, 2, 3, 4
ORDER BY conns DESC;
Real output from the scratch server while I held one transaction open:
usename | application_name | client_addr | state | conns | longest_in_state
----------+------------------+-------------+---------------------+-------+------------------
app | psql | 127.0.0.1 | active | 1 | 00:00:02.02878
app | psql | 127.0.0.1 | idle in transaction | 1 | 00:00:02.02902
ops | psql | 127.0.0.1 | idle | 1 | 00:00:02.029075
Then check headroom against the configured limits:
SELECT current_setting('max_connections')::int AS max_conn,
current_setting('superuser_reserved_connections')::int AS su_reserved,
current_setting('reserved_connections')::int AS reserved, -- PG 16+; remove on older versions
count(*) FILTER (WHERE backend_type = 'client backend') AS in_use
FROM pg_stat_activity;
Read the state column like this. idle means a pool is holding connections it is not using, which is normal in small numbers and a sizing problem in large ones. idle in transaction means application code opened a transaction and wandered off; these also hold locks and stop vacuum. active with a long longest_in_state means slow queries are the real problem and connections are piling up behind them. If one client_addr or application_name dominates, you have your culprit.
Step 2: Kill idle in transaction sessions and set timeouts
To recover now, terminate sessions that have sat idle inside a transaction for more than a few minutes. This rolls back their open work, so check what they are first if the data matters.
-- Look before you kill
SELECT pid, usename, application_name, now() - state_change AS idle_for, left(query, 80) AS last_query
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'idle in transaction (aborted)')
AND state_change < now() - interval '5 minutes'
ORDER BY idle_for DESC;
-- Terminate them (needs superuser or pg_signal_backend)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'idle in transaction (aborted)')
AND state_change < now() - interval '5 minutes'
AND backend_type = 'client backend';
Then stop it recurring. Set the timeouts on application roles rather than server-wide, so your own maintenance sessions are not caught. All of these default to 0 (disabled) per the client connection defaults docs:
ALTER ROLE app SET idle_in_transaction_session_timeout = '60s';
ALTER ROLE app SET statement_timeout = '30s';
-- PostgreSQL 17+ only: caps the whole transaction, idle or not
ALTER ROLE app SET transaction_timeout = '5min';
Role settings apply to new sessions, so recycle the application's pool afterwards. Be careful with idle_session_timeout: the docs warn that poolers "may not react well to unexpected connection closure", so leave it off for roles that connect through PgBouncer or an application pool.
Step 3: Do the max_connections math
Before touching max_connections, add up what legitimately needs a backend. A worked example for one database, 6 application pods with an in-process pool of 10 each, plus a migration job and monitoring:
direct today: 6 pods x 10 pool = 60
migrations / cron = 5
monitoring = 2
superuser_reserved = 3 (default)
reserved_connections = 5 (Step 4)
----
minimum max_connections = 75 -> fine at the default 100
after an autoscale to 20 pods: 20 x 10 = 200 -> error, whatever the CPU says
That second line is why raising the number is a trap: the requirement scales with pod count, not load. Each Postgres backend is a process and the server sizes shared memory from max_connections, and changing it needs a restart, so you cannot tune your way out mid-incident. For sizing the working pool, the PostgreSQL wiki formula quoted on HikariCP's pool sizing page, (core_count * 2) + effective_spindle_count, gives about 16 to 20 busy backends on an 8-core server. On RDS the default is LEAST({DBInstanceClassMemory/9531392}, 5000) per the RDS quotas page.
Step 4: Reserve slots for operators with pg_use_reserved_connections
On Postgres 16 and later, give your on-call and monitoring roles their own slots so the next incident does not lock you out. reserved_connections defaults to 0 and must be less than max_connections minus superuser_reserved_connections.
ALTER SYSTEM SET reserved_connections = 5; -- takes effect after restart
GRANT pg_use_reserved_connections TO ops; -- your on-call / monitoring role
# Self-managed Postgres 18 on Debian/Ubuntu; on RDS, change the parameter group instead
sudo systemctl restart postgresql@18-main
psql -U postgres -Atc "SHOW reserved_connections;" # expect: 5
Step 5: Deploy PgBouncer 1.26.1 in transaction mode
PgBouncer turns thousands of client connections into a few dozen backends. Install it from the PostgreSQL apt repository (PGDG), which carries 1.26.1 for Ubuntu 24.04:
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh # adds the PGDG repo
sudo apt install -y pgbouncer=1.26.1-1.pgdg24.04+1
pgbouncer --version # first line should read: PgBouncer 1.26.1
If you prefer a container, the community image edoburu/pgbouncer:v1.26.1-p0 builds the same release; the pgbouncer/pgbouncer image on Docker Hub was still on 1.25.2 when I checked, which misses the CVE fixes in 1.26.0 and 1.26.1.
Create the auth file. Postgres 14+ stores SCRAM secrets by default, and PgBouncer can use them directly:
-- run as superuser on the Postgres server
SELECT format('"%s" "%s"', rolname, rolpassword)
FROM pg_authid
WHERE rolname IN ('app', 'pgbouncer_admin');
# paste the two lines from the query above
sudo tee /etc/pgbouncer/userlist.txt >/dev/null <<'EOF'
"app" "SCRAM-SHA-256$4096:REPLACE_WITH_SECRET_FROM_QUERY"
"pgbouncer_admin" "SCRAM-SHA-256$4096:REPLACE_WITH_SECRET_FROM_QUERY"
EOF
sudo chown postgres:postgres /etc/pgbouncer/userlist.txt
sudo chmod 600 /etc/pgbouncer/userlist.txt
Then the config. Every setting below is from the PgBouncer configuration reference:
; /etc/pgbouncer/pgbouncer.ini (PgBouncer 1.26.1)
[databases]
appdb = host=10.0.1.20 port=5432 dbname=appdb
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
admin_users = pgbouncer_admin
pool_mode = transaction ; default is session, which does not multiplex
max_client_conn = 2000 ; default 100; raise LimitNOFILE to match
default_pool_size = 40 ; default 20, per user/database pair
max_db_connections = 45 ; hard cap on backends for this database
reserve_pool_size = 5
reserve_pool_timeout = 3 ; default 5 seconds
max_prepared_statements = 200 ; default since 1.24.0
query_wait_timeout = 120 ; default; queued clients are dropped after this
idle_transaction_timeout = 60 ; default 0 (off)
server_tls_sslmode = require ; default is prefer
sudo systemctl restart pgbouncer
sudo systemctl status pgbouncer --no-pager | head -5
psql "host=127.0.0.1 port=6432 dbname=appdb user=app" -c "select 1"
Now point the application at port 6432. In transaction mode a backend changes hands after every transaction, so the app must not rely on session state: no SET outside a transaction (use ALTER ROLE ... SET or SET LOCAL), no session advisory locks, no LISTEN, no SQL-level PREPARE. The full list is on the PgBouncer features page. If one component needs LISTEN, give it a second [databases] entry with pool_mode=session and a small pool_size.
Step 6: Size the application pools
With PgBouncer in front, application pools can be large on the client side and the database still sees at most max_db_connections. Two rules:
# Example: per-pod pool settings (names vary by driver; values are what matter)
pool:
max_size: 10 # clients to PgBouncer; cheap, about 2 kB each on the pooler
min_idle: 0 # do not pin idle connections on every pod
max_lifetime: 30m # recycle so PgBouncer restarts and failovers drain cleanly
connection_timeout: 5s # fail fast instead of piling up behind a full pool
First, the sum of all pod pools must fit under max_client_conn. Second, default_pool_size times pooler instances (plus reserves) must fit under Postgres max_connections minus the slots from Step 3. If connections are mostly active and slow, no pool size helps: find the slow queries with pg_stat_statements and EXPLAIN (ANALYZE, BUFFERS), as in the guide to finding slow Postgres queries.
Step 7: Verify with SHOW POOLS
Connect to PgBouncer's admin console (the special pgbouncer database) and watch the pools under load:
psql "host=127.0.0.1 port=6432 dbname=pgbouncer user=pgbouncer_admin" -c "SHOW POOLS;"
The usage docs define the columns. cl_active and cl_waiting are client connections linked or queued; sv_active, sv_idle and sv_used are backends. The one to alert on is maxwait, the wait of the oldest queued client in seconds: if it keeps rising, the pool is too small or the server is overloaded. Back on Postgres, Step 1's query should now show one client_addr (the pooler) holding at most max_db_connections backends.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
remaining connection slots are reserved for roles with the SUPERUSER attribute |
Non-reserved slots full | Step 1, then Step 2 or Step 5; do not just raise max_connections |
| Error returns minutes after a restart | A deploy or autoscale multiplied per-pod pools | Do the Step 3 math; put PgBouncer in front |
Hundreds of idle in transaction rows |
App opens a transaction then waits (HTTP or LLM call) | pg_terminate_backend, idle_in_transaction_session_timeout on the role |
no more connections allowed (max_client_conn) from PgBouncer |
Client side of the pooler is full | Raise max_client_conn and the systemd LimitNOFILE; check for leaked app connections |
Clients disconnected with query_wait_timeout |
All backends busy for 120 s | Check maxwait in SHOW POOLS; fix slow queries or raise default_pool_size within budget |
prepared statement "S_1" does not exist |
PgBouncer older than 1.21, max_prepared_statements = 0, or SQL-level PREPARE |
Upgrade to 1.26.1, keep the default 200, use protocol-level prepares |
| Queries hit the wrong schema after enabling the pooler | SET search_path leaking across clients (fixed for PG 18 in 1.26.0) |
Upgrade; prefer ALTER ROLE ... SET search_path |
LISTEN stops receiving notifications |
Transaction mode hands the backend to another client | Separate database entry with pool_mode=session |
Pooler login fails with SASL authentication failed |
Wrong or stale secret in userlist.txt |
Re-run the pg_authid query after password changes and RELOAD |
FAQ
Should I just increase max_connections?
Only as a stopgap, and only if memory allows. It needs a restart, each backend is a process, and if pods autoscale the new limit will be reached again. Put a pooler in front and size from cores, not from client count.
What is the difference between idle and idle in transaction?
idle is a connection waiting for its next query with no open transaction; it costs a slot but holds no locks. idle in transaction has an open transaction, so it holds locks and stops vacuum from cleaning up dead rows, which makes it far more harmful. Set idle_in_transaction_session_timeout for application roles.
Does PgBouncer work with RDS and Aurora?
Yes, run it on EC2, ECS or Kubernetes in front of the RDS endpoint, using the same config. RDS Proxy is the managed alternative, but SET, temp tables and SQL prepared statements pin its sessions, which reduces how much it multiplexes.
How do I find which application is leaking connections?
Group pg_stat_activity by application_name and client_addr, as in Step 1. Make every service set application_name in its connection string so the answer is a name rather than a pod IP.
When this error pages me, I run the Step 1 query before anything else, because the state column tells me in ten seconds whether it is a leak, a sizing problem or slow queries wearing a connection costume. In production I want PgBouncer in transaction mode in front of every application path, role-level timeouts on every app and agent role, and five reserved slots that only on-call can use. The official references are the PostgreSQL connection settings, client timeouts, the PgBouncer configuration reference and the PgBouncer changelog. For the reasoning behind the pool modes, see Postgres connection pooling explained.
Related: How to Run Postgres on Kubernetes with CloudNativePG 1.30 (2026), How to Run Zero-Downtime Postgres Migrations: Step by Step (2026) and How to Fix Kubernetes CrashLoopBackOff: Causes and Commands (2026).
Spot an error or something out of date? Email me and I will fix it and log it on the corrections page.
I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.
Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.
Related
How to Find Slow Queries in Postgres: pg_stat_statements and EXPLAIN (2026)
Step-by-step guide to finding and fixing slow PostgreSQL queries on Postgres 18: enable pg_stat_statements, rank queries by total time, read EXPLAIN ANALYZE plans and buffers, add the right index concurrently, fix bad estimates and log slow plans with auto_explain.
12 minOct 20, 2026How to Troubleshoot Linux Performance in 60 Seconds (2026 Checklist)
A step-by-step Linux performance troubleshooting guide for 2026: Brendan Gregg's 60-second checklist (uptime, dmesg, vmstat, mpstat, pidstat, iostat, free, sar, top), how to read each output, PSI, the USE method, then bpftrace 0.27 and BCC one-liners.
13 minNov 10, 2026How to Fix AWS ALB Unhealthy Targets and 502/503 Errors (2026)
Fix AWS Application Load Balancer unhealthy targets and 502, 503 and 504 errors step by step: read the health reason code, fix health check paths and success codes, security groups, ip vs instance targets, EKS readiness gates, deregistration delay, keep-alive timeouts and access logs.
13 min