"FATAL: sorry, too many clients already" from NetBox under load: Gunicorn workers vs PostgreSQL max_connections
0 reputation · 23 Mar 2021, 21:37 UTC
0 reputation · 23 Mar 2021, 21:37 UTC
Under concurrent request load, a NetBox deployment intermittently logs django.db.utils.OperationalError: FATAL: sorry, too many clients already. The error surfaces from the Django ORM when a request handler tries to acquire a database connection, and it correlates with the PostgreSQL max_connections limit being reached rather than with any single slow query.
The deployment runs NetBox (current 4.x line assumed) with Gunicorn sync workers and the default DATABASE_CONN_MAX_AGE of 0, so each worker process opens and closes its own connections per request. PostgreSQL max_connections is set to a modest value, and SELECT count(*) FROM pg_stat_activity; shows active connections climbing toward the limit before errors appear.
The unresolved decision is how to size and shape the connection footprint without just masking the problem:
max_connections, or raising the PostgreSQL limit to match the existing worker count?CONN_MAX_AGE above 0 actually reduce exhaustion here, or does it risk stale-connection errors ("server closed the connection unexpectedly") after database restarts or idle timeouts?31275 reputation · 24 Mar 2021, 04:07 UTC
Each Gunicorn sync worker opens a new PostgreSQL connection for every request when DATABASE_CONN_MAX_AGE is 0 (the NetBox default). Under load, the total number of concurrent connections approximates:
workers × average simultaneous requests per worker
When this product exceeds the PostgreSQL max_connections setting, the server replies with FATAL: sorry, too many clients already, which Django surfaces as an OperationalError.
max_connections or lowering the Gunicorn worker count directly reduces the chance of hitting the limit.CONN_MAX_AGE > 0 enables connection reuse inside a worker, lowering new connections per request, but can cause server closed the connection unexpectedly if PostgreSQL restarts or the idle timeout exceeds the age.gevent, eventlet) can share a single connection pool across many concurrent requests, lowering the connection count, but NetBox plugin compatibility is not guaranteed and should be tested.SELECT count(*) FROM pg_stat_activity;
and compare the result to SHOW max_connections; to confirm exhaustion.gunicorn --statsd-host). This figure is needed to decide whether adjusting workers or max_connections is more appropriate.max_connections to safely accommodate workers × concurrency.max_connections is undesirable (memory/context‑switch concerns), reduce the Gunicorn worker count so that the product stays below the limit.DATABASE_CONN_MAX_AGE=60 (or another suitable value) and verify that connection churn drops. Then simulate a PostgreSQL restart to ensure no server closed the connection unexpectedly errors appear.gunicorn -k gevent (or eventlet) and monitor connection counts. Roll back if any plugin exhibits errors.Missing diagnostic detail: To finalize the recommendation between lowering workers versus raising max_connections, please provide the measured average simultaneous requests per Gunicorn worker under load.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.