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_waiting for 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 SHOW output, 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

CauseWhat it looks likeFirst thing to check
Application connection leakused_clients climbs steadily over hours or days, never returns to baselineSHOW CLIENTS for connections with very old connect_time
Uncoordinated deploymentsused_clients steps up each time a new app version or instance rolls outClient count by source IP in SHOW CLIENTS vs expected instance count x pool size
max_client_conn exceeds the FD limitRefusals start well below the configured max_client_connPgBouncer startup log line reporting the FD limit and effective max_client_conn
Retry storm during an incidentSudden spike in used_clients while another failure (pool exhaustion, backend down) is in progressWhether cl_waiting or sv_login was elevated just before refusals began
Limit simply too smallSlow organic growth; utilization sits above 80% at peak for weeksused_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

  1. 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 see query_wait_timeout or clients reporting long waits after connecting, you are dealing with pool exhaustion, a different incident with different fixes. See PgBouncer pool exhaustion.

  2. Check pool health to rule out compounding causes. Run SHOW POOLS. If sv_active is well below pool_size and cl_waiting is zero, the pools are healthy and the problem is purely the front door. If cl_waiting was high before refusals began, the client ceiling may be a secondary effect: applications timed out waiting, retried, opened new connections, and piled onto max_client_conn. Fix the pool problem first.

  3. Identify who holds the connections. Run SHOW CLIENTS and group by addr (source IP) and connect_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).

  4. 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 files in /proc/<pid>/limits, or LimitNOFILE under systemd) is lower than what max_client_conn requires, 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 effective max_client_conn near 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.

  5. Check for other client limits. max_db_client_connections (per database) and max_user_client_connections (per user) also reject new clients when exceeded, and both default to 0 (unlimited). If they are set, check SHOW CONFIG to see whether one of them is the binding constraint.

Metrics and signals to monitor

SignalWhy it mattersWarning 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 ratioBelow 10% of max_client_conn
Log: no more connections allowedThe only record of actual refusalsAny sustained occurrence
cl_waiting and maxwait (SHOW POOLS)Tells you whether a pool problem is driving the retry stormNon-zero before or during refusals
Process FD count vs Max open files (/proc/<pid>/)FDs can run out before max_client_conn doesAbove 80% of limit
SHOW CLIENTS oldest connect_timeLeak detection: connections older than any legitimate sessionConnections 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 CLIENTS by 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_timeout can 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 LimitNOFILE in 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_conn in pgbouncer.ini) applies with RELOAD, but it cannot exceed what the FD limit allows.
  • Plan for rolling restarts with multiple PgBouncer processes on so_reuseport if 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_conn above 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_conn matches 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_conn as 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 LISTS and SHOW CONFIG data, so used_clients against max_client_conn is 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_clients flags slow leaks that sit below static thresholds for weeks before they become an incident.