Your application logs fill with connection errors, and every new connection attempt to PgBouncer fails immediately with:
ERROR: no more connections allowed (max_client_conn)
This is not pool exhaustion. The client never gets in the door. There is no queue, no wait, no query_wait_timeout. PgBouncer counts the client connection, sees it would exceed max_client_conn, and refuses it on the spot. Existing clients keep working; only new ones are turned away.
The confusing part is that the pool itself may be completely healthy. sv_active can be well below pool_size, cl_waiting can be zero, and avg_wait_time can be flat. PostgreSQL is fine, the pools are fine, and yet applications cannot connect. The bottleneck is the front door, not the backend.
What this means
max_client_conn is a hard ceiling on the number of simultaneous client connections PgBouncer will accept (default: 100). When used_clients reaches it, every additional connection attempt is rejected, logged by PgBouncer as no more connections allowed (max_client_conn).
Two properties make this failure mode distinct:
- No queuing. In pool exhaustion, the client connects successfully and then waits in
cl_waitingfor a server connection. Here, the client is refused before any of that machinery engages. That is why your pool metrics look green during the incident. - No metric. PgBouncer has no SHOW counter for refused connections. The refusal exists only in the log file and in client-side errors. If you monitor only
SHOWoutput, the first signal you get is applications failing.
Admin connections to the special pgbouncer database are exempt from max_client_conn, so you can still get in and diagnose while clients are being refused.
flowchart TD
A[New client connection] --> B{used_clients at max_client_conn?}
B -- Yes --> C[Rejected instantly: no more connections allowed]
B -- No --> D{Server connection available in pool?}
D -- Yes --> E[Query executes]
D -- No --> F[Client joins cl_waiting queue]
F --> G[query_wait_timeout if wait is too long]Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Application connection leak | used_clients climbs steadily over hours or days, never returns to baseline | SHOW CLIENTS for connections with very old connect_time |
| Uncoordinated deployments | used_clients steps up each time a new app version or instance rolls out | Client count by source IP in SHOW CLIENTS vs expected instance count x pool size |
max_client_conn exceeds the FD limit | Refusals start well below the configured max_client_conn | PgBouncer startup log line reporting the FD limit and effective max_client_conn |
| Retry storm during an incident | Sudden spike in used_clients while another failure (pool exhaustion, backend down) is in progress | Whether cl_waiting or sv_login was elevated just before refusals began |
| Limit simply too small | Slow organic growth; utilization sits above 80% at peak for weeks | used_clients / max_client_conn trend over time |
Quick checks
All commands are read-only. Adjust host, port, and user for your environment.
# 1. Confirm refusals in the log (the only place they are recorded)
grep -c "no more connections allowed" /var/log/pgbouncer/pgbouncer.log
tail -200 /var/log/pgbouncer/pgbouncer.log | grep "no more connections allowed"
# 2. Check current client utilization and the configured limit
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer -Atc "SHOW LISTS;"
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer -Atc "SHOW CONFIG;" | grep max_client_conn
# 3. Verify the pools are actually healthy (distinguishes from pool exhaustion)
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer -Atc "SHOW POOLS;"
# 4. Look for leaked client connections (old connect_time, idle state)
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer -c "SHOW CLIENTS;"
# 5. Check the actual file descriptor situation
PGBPID=$(pgrep -f pgbouncer)
ls /proc/$PGBPID/fd | wc -l
grep "Max open files" /proc/$PGBPID/limits
Interpreting SHOW LISTS: used_clients is the count of active client objects. free_clients is the pre-allocated slab of client structures and drops toward zero as clients connect; when used_clients reaches max_client_conn, refusals begin. Treat used_clients / max_client_conn as the utilization signal, with free_clients as corroboration.
How to diagnose it
Confirm the symptom class. Grep the log for
no more connections allowed. If it is present, you are at the client ceiling. If instead you seequery_wait_timeoutor clients reporting long waits after connecting, you are dealing with pool exhaustion, a different incident with different fixes. See PgBouncer pool exhaustion.Check pool health to rule out compounding causes. Run
SHOW POOLS. Ifsv_activeis well belowpool_sizeandcl_waitingis zero, the pools are healthy and the problem is purely the front door. Ifcl_waitingwas high before refusals began, the client ceiling may be a secondary effect: applications timed out waiting, retried, opened new connections, and piled ontomax_client_conn. Fix the pool problem first.Identify who holds the connections. Run
SHOW CLIENTSand group byaddr(source IP) andconnect_time. Leaked connections show up as long-lived connections from application hosts that far outnumber the configured application pool size. A deployment problem shows up as more source IPs than you expect, or roughly double the expected connections per instance (old and new versions running side by side).Check the FD limit before assuming the config value is real. PgBouncer needs one FD per client connection, plus one per server connection, plus listening sockets, the log file, pipe FDs, and admin sockets. If the OS limit (
Max open filesin/proc/<pid>/limits, orLimitNOFILEunder systemd) is lower than whatmax_client_connrequires, PgBouncer may lower the effective limit at startup or hit the FD wall first. Check the startup log: PgBouncer logs its detected FD limit and the effectivemax_client_connnear the top of the log after each start. If the logged value is lower than your configured value, the FD limit is the real ceiling.Check for other client limits.
max_db_client_connections(per database) andmax_user_client_connections(per user) also reject new clients when exceeded, and both default to 0 (unlimited). If they are set, checkSHOW CONFIGto see whether one of them is the binding constraint.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
used_clients / max_client_conn (SHOW LISTS + SHOW CONFIG) | Utilization ratio for the front door; failure is binary at 100% | Above 80% sustained |
free_clients (SHOW LISTS) | Remaining pre-allocated client slots; corroborates the ratio | Below 10% of max_client_conn |
Log: no more connections allowed | The only record of actual refusals | Any sustained occurrence |
cl_waiting and maxwait (SHOW POOLS) | Tells you whether a pool problem is driving the retry storm | Non-zero before or during refusals |
Process FD count vs Max open files (/proc/<pid>/) | FDs can run out before max_client_conn does | Above 80% of limit |
SHOW CLIENTS oldest connect_time | Leak detection: connections older than any legitimate session | Connections hours or days old |
Fixes
Fix the application connection leak
The most common root cause. Connections are opened and never closed: a code path that skips close() on error, an ORM pool with no maximum lifetime, or a worker that leaks a connection per job.
- Identify the offenders from
SHOW CLIENTSby source IP and connection age, then fix the application code or pool configuration (maximum lifetime, idle timeout, pool size ceiling). - As a mitigation while the fix rolls out,
client_idle_timeoutcan reclaim idle client connections. Understand what it will kill before enabling it: any client that legitimately sits idle longer than the timeout gets disconnected. - Do not restart PgBouncer to clear leaked connections as a first move. Restarting drops all server connections and triggers a thundering herd of reconnects against PostgreSQL, and the leak will refill the ceiling within hours anyway.
Raise max_client_conn (safely)
If demand is legitimate, raising the limit is correct, but only after the FD math checks out.
- The FD budget is roughly:
max_client_conn + total server connections + listening sockets + log FD + pipe FDs + admin sockets. Keep at least 20% headroom beyond that sum. - If the OS limit needs raising, set
LimitNOFILEin the systemd unit (or the ulimit for the PgBouncer user). The FD limit is fixed at process start, so this change requires a PgBouncer restart to take effect. - The config change itself (
max_client_conninpgbouncer.ini) applies withRELOAD, but it cannot exceed what the FD limit allows. - Plan for rolling restarts with multiple PgBouncer processes on
so_reuseportif a full restart is disruptive.
Coordinate deployments
If used_clients steps up with each deploy, the problem is arithmetic: application instances x connections per instance is not accounted for in max_client_conn. Compute the worst case (all current instances plus a full extra batch during a rolling deploy) and size for that, or cap per-instance pool sizes so the product fits.
Break a retry storm
If refusals are a symptom of pool exhaustion plus application retries, raising max_client_conn alone will not help; it just lets more clients queue. Fix the pool saturation first (slow queries, idle-in-transaction, undersized pool_size), and reduce application-side retry aggressiveness. See PgBouncer query_wait_timeout and PgBouncer maxwait high.
Prevention
- Alert on the ratio, not the refusal.
used_clients / max_client_connabove 80% sustained is a ticket; above 95% is urgent. The refusal itself is only visible in logs, so the ratio is your early warning. - Alert on FD headroom too. FD count above 80% of the limit catches the case where the real ceiling is lower than the configured one.
- Verify the startup log after every restart. Confirm the logged effective
max_client_connmatches what you configured. This catches silent FD capping immediately. - Budget connections as code. Every new application deployment should declare its connection footprint. Track
max_client_connas a capacity line item, not a set-and-forget config value. See PgBouncer capacity planning. - Ship PgBouncer logs somewhere queryable. Since refusals, auth failures, and timeouts exist only in the log, log aggregation is not optional for this service.
How Netdata helps
- Netdata collects
SHOW LISTSandSHOW CONFIGdata, soused_clientsagainstmax_client_connis graphed continuously, letting you see the slow leak or the deployment step-change instead of discovering it at 100%. - Pool metrics (
sv_active,sv_idle,cl_waiting,maxwait) on the same dashboard let you confirm in seconds that this is a front-door problem and not pool exhaustion, the key diagnostic branch. - Host-level file descriptor collection alongside PgBouncer metrics exposes the FD-vs-config gap that silently caps
max_client_conn. - Because PgBouncer exposes no refusal counter, correlating the utilization ratio crossing 100% with the moment application error rates spiked is how you reconstruct the incident timeline; per-second collection makes that correlation tight.
- Anomaly detection on
used_clientsflags slow leaks that sit below static thresholds for weeks before they become an incident.
Related guides
- PgBouncer advisory locks in transaction mode: orphaned locks and mysterious contention
- PgBouncer avg_wait_time high: the latency the pool itself is injecting
- PgBouncer capacity planning: runway for pools, clients, and PostgreSQL slots
- How PgBouncer actually works in production: a mental model for operators
- PgBouncer LISTEN/NOTIFY not working: why pub/sub needs session pooling
- PgBouncer maxwait high: the oldest client waiter and how close it is to timing out
- PgBouncer monitoring checklist: the signals every connection pooler needs
- PgBouncer monitoring maturity model: from survival to expert
- PgBouncer pool exhaustion: clients queue, wait times climb, and the retry cascade
- PgBouncer pool utilization high: sv_active approaching pool_size before clients queue
- PgBouncer prepared statement does not exist: transaction pooling and lost session state
- PgBouncer query_wait_timeout: clients disconnected after waiting too long for a connection






