Guides / FATAL: sorry, too many clients already (Django and Postgres)
FATAL: sorry, too many clients already (Django and Postgres)
Postgres refuses new connections with FATAL: sorry, too many clients already or FATAL: remaining connection slots are reserved for non-replication superuser connections. Your app is opening more database connections than the server allows. Count them, then reduce them or pool them.
What it means
Postgres has a hard limit (max_connections, often 100 on small managed databases, and much lower on the smallest plans). Every Django worker thread keeps its own connection. Add instances and the number multiplies. Hitting the limit makes new requests fail while the rest of the app seems fine.
Why it happens
- Workers × threads × instances is larger than the limit. 4 gunicorn workers × 4 threads × 3 instances = 48 connections before anything else connects.
- Persistent connections that are never closed.
CONN_MAX_AGE = Nonekeeps every connection open forever, including from idle workers. - Overlapping deploys. The old and the new instances run together for a while, doubling the count.
- Other clients: a shell, a migration, a BI tool or a scheduled script left connected.
- Connections leaked by threads you start yourself. Django closes connections at the end of a request, but not in your own
threading.Threador management commands that loop forever.
The fix
First look at the actual numbers:
SHOW max_connections;
SELECT count(*) FROM pg_stat_activity;
SELECT usename, application_name, state, count(*)
FROM pg_stat_activity GROUP BY 1, 2, 3 ORDER BY 4 DESC;
Then apply, in this order:
1. Do the arithmetic and cut it down
# connections ~ instances x workers x threads (keep it well below max_connections)
gunicorn myproject.wsgi --workers 3 --threads 2
2. Use a short CONN_MAX_AGE with health checks
DATABASES["default"]["CONN_MAX_AGE"] = 60 # seconds; 0 closes after every request
DATABASES["default"]["CONN_HEALTH_CHECKS"] = True # drop stale connections before reuse
Never use None in production. With 0, every request opens a new connection: fine for low traffic, wasteful at high traffic.
3. Pool connections
If you still need more app threads than database connections, put a pooler between them. With Django 5.1 or newer and psycopg 3 you can pool inside Django:
pip install "psycopg[binary,pool]"
DATABASES["default"]["OPTIONS"] = {"pool": True}
# set CONN_MAX_AGE to 0 (the default) when using pool=True
On any version, PgBouncer (or your provider's pooler) in transaction mode is the standard answer. In that mode turn off server-side cursors, which don't work through it:
DATABASES["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
4. Or raise the limit
A bigger database plan usually allows more connections. It is a valid fix when the app really needs them, but connections cost memory on the server, so pooling is cheaper than size.
Making it stick in production
- Write the formula down next to your gunicorn command, and revisit it whenever you add instances or autoscaling.
- Remember deploys: during a rolling release both versions hold connections. Leave 30 to 50 percent headroom.
- Give each client a name so you can see who holds connections:
"OPTIONS": {"application_name": "web"}. - Alert on
count(*)frompg_stat_activityreaching about 80 percent ofmax_connections.
Or skip it
Django Cloud connects a managed Postgres database for you, and lets you change the server size or run 1 to 20 instances from the dashboard or with djangocloud scale, once you know how many connections each instance needs.
pip install djangocloud-cli
djangocloud deploy
Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.
Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.