Fix PostgreSQL: sorry, too many clients already
Learn how to diagnose PostgreSQL connection-limit errors, distinguish leaks from traffic spikes, and fix them safely with pool sizing and PgBouncer.
ArmorDB Engineering
ArmorDB engineering
On this page 7 sections
FATAL: sorry, too many clients already means PostgreSQL refused a new session because the server's connection capacity was full. The error often appears during a deploy, import, cron burst, serverless traffic spike, or application leak. It can also show up as remaining connection slots are reserved for roles with privileges of the pg_use_reserved_connections role or as a provider-specific connection-limit message.
The important point is that this is a capacity and lifecycle problem, not a bad password or SQL syntax problem. Retrying every request immediately usually makes the incident worse because each retry tries to open another connection. The safe fix is to identify who is holding sessions, stop new connection storms, and then right-size application pools or add a pooler such as PgBouncer.
Fast diagnosis
If you can still connect with an admin user, start with pg_stat_activity. It shows current backend sessions, the database and user attached to each one, the client address, session state, and query timing. Run a compact count first so you do not scroll through hundreds of rows during an incident:
SELECT datname, usename, application_name, state, count(*)
FROM pg_stat_activity
GROUP BY 1, 2, 3, 4
ORDER BY count(*) DESC;
Then look for old idle sessions and transactions that have been open too long:
SELECT pid, usename, application_name, client_addr, state,
now() - state_change AS state_age,
now() - xact_start AS transaction_age
FROM pg_stat_activity
WHERE state <> 'active' OR xact_start IS NOT NULL
ORDER BY greatest(now() - state_change, now() - coalesce(xact_start, state_change)) DESC
LIMIT 25;
This separates three common cases. A real traffic spike usually has many active sessions from the same application tier. A pool-size problem often has too many idle sessions spread across many app instances. A leak often has connections whose state_change age keeps growing long after the request that opened them should have finished.
Symptom in pg_stat_activity | Likely cause | Safest immediate action | Durable fix |
|---|---|---|---|
Many active sessions from one job | Import, migration, or worker burst | Pause or reduce the job concurrency | Add explicit worker limits and backpressure |
Many idle sessions across app hosts | Pool size multiplied by replicas | Restart only the offending app tier if needed | Lower per-instance pool size or use PgBouncer |
Long idle in transaction sessions | Code opened a transaction and stopped | Cancel or terminate after checking impact | Fix transaction scope and timeouts |
| Failures only during deploys | Old and new pods overlap pools | Slow rollout or temporarily reduce replicas | Account for max surge in pool math |
| Admins cannot connect | All ordinary slots are consumed | Use provider console or reserved/admin path | Keep application roles away from reserved slots |
Do not fix it by only raising max_connections
PostgreSQL exposes max_connections, and the official documentation describes reserved connection settings such as reserved_connections and superuser_reserved_connections. Raising the limit can be appropriate when the instance has enough memory and the workload truly needs more concurrency, but it is rarely the first fix. Every backend session consumes server resources, and hundreds of mostly idle application sessions can still make failover, memory planning, and incident response harder.
For managed PostgreSQL, the practical answer is usually to reduce direct database sessions rather than to let every process talk to the server independently. A web app with 20 replicas and a default pool of 20 can try to hold 400 database sessions before background workers, migrations, dashboards, and admin tools are counted. If the database plan allows fewer connections than that, the outage is predictable even when average query volume is modest.
Calculate the pool budget
Set a connection budget before tuning any individual library. Leave room for migrations, admin access, monitoring, and a failover or deploy overlap. Then divide the application share by the maximum number of processes that can be alive at the same time, not just the number you normally run.
A simple starting formula is: direct app pool size equals (database connection limit - reserved operational headroom) / maximum concurrent app processes. If the answer is tiny, that is not a sign to ignore the math. It is a sign to use a transaction pooler, reduce app process count, or move bursty workers behind a queue with explicit concurrency.
PgBouncer helps because it accepts many client connections while keeping a smaller number of server connections open to PostgreSQL. Its own documentation distinguishes settings such as max_client_conn, default_pool_size, and pool_mode. For most request/response web applications, transaction pooling is often the useful shape: a server connection returns to the pool when a transaction finishes. Session features, prepared statement behavior, temporary tables, and advisory locks need review before switching modes, so do not flip pooling in production without testing the application path that uses those features.
ArmorDB includes PgBouncer for this reason. If connection pressure is the incident pattern, compare the direct connection string with the pooled one in /docs/pgbouncer, and keep migrations or admin scripts on the connection type that matches their behavior.
Immediate recovery checklist
First, stop the source of new connections. Pause a runaway job, scale down a noisy worker, or halt an aggressive rollout. If retries are hammering the database, increase backoff in the application or at the queue layer before restarting everything.
Second, identify safe sessions to remove. Terminating a session rolls back its active transaction, so prefer to cancel obvious long-running statements first and terminate only sessions that are clearly abandoned, idle too long, or owned by the broken deploy. Use application names in connection strings so this decision is possible under pressure.
Third, reconnect cleanly. Restarting a web tier can clear leaked sessions, but it can also create a thundering herd if every instance starts at once. Bring instances back gradually with a lower pool size, verify the count in pg_stat_activity, and only then restore worker concurrency.
Common mistakes
One mistake is counting only one application process. Autoscaling, rolling deploy max surge, preview environments, background queues, BI tools, and migration containers all consume the same finite server slots when they connect directly. Another mistake is leaving the pool default untouched. Many client libraries choose defaults that are reasonable for a single process but unsafe when multiplied across containers.
A third mistake is treating idle sessions as free. Idle is better than active, but idle sessions still occupy connection slots. In a managed database, the goal is not to eliminate every idle session; it is to keep the number bounded and explainable.
Sources and further reading
- PostgreSQL documentation:
max_connections,reserved_connections, andsuperuser_reserved_connectionsin runtime connection settings. - PostgreSQL documentation:
pg_stat_activityin monitoring statistics. - PgBouncer documentation:
pool_mode,default_pool_size, andmax_client_connconfiguration. - ArmorDB docs: /docs/pgbouncer for using the pooled endpoint.
Takeaway
too many clients already is PostgreSQL protecting itself from unbounded sessions. Recover by stopping the connection storm, finding who owns the slots, and clearing only the sessions you understand. Prevent the repeat by budgeting connections across every app process, lowering per-instance pool sizes, and using PgBouncer when many clients need to share a smaller, healthier number of PostgreSQL backends.
Written by ArmorDB Engineering
Practical notes on PostgreSQL operations, security, and infrastructure decisions for teams building production applications.
Updated Aug 28, 2026
Keep exploring
Related reading
Quick Fixes · 5 min read
Fix PostgreSQL: Remaining Connection Slots Are Reserved
A practical quick fix for PostgreSQL's reserved connection slot error, including what to check, how to recover safely, and when to add pooling.
Read articleQuick Fixes · 8 min read
How to Fix PostgreSQL too many clients already Errors
A short fix guide for connection-limit errors, idle sessions, and when to add PgBouncer instead of raising max_connections.
Read articleQuick Fixes · 5 min read
Fix PostgreSQL: cannot execute INSERT in a read-only transaction
Learn why PostgreSQL reports a read-only transaction, how to confirm whether you are on a replica or read-only session, and the safest fix for managed databases.
Read article