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

  1. Workers × threads × instances is larger than the limit. 4 gunicorn workers × 4 threads × 3 instances = 48 connections before anything else connects.
  2. Persistent connections that are never closed. CONN_MAX_AGE = None keeps every connection open forever, including from idle workers.
  3. Overlapping deploys. The old and the new instances run together for a while, doubling the count.
  4. Other clients: a shell, a migration, a BI tool or a scheduled script left connected.
  5. Connections leaked by threads you start yourself. Django closes connections at the end of a request, but not in your own threading.Thread or 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

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

Get startedSee pricing

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.