Back to Blog

OperationalError: Django and PostgreSQL Connection Errors, Decoded

admin
August 7, 2026 3 min read
32 views
Connection refused, password authentication failed, too many connections, SSL required — the five PostgreSQL errors Django surfaces most often, what each one really means, and the fix.

OperationalError: Django and PostgreSQL Connection Errors, Decoded

OperationalError is Django saying "the database did not cooperate" without telling you why. The
detail is in the message underneath, and each variant has a distinct cause.

connection refused

could not connect to server: Connection refused
Is the server running on host "localhost" and accepting TCP/IP connections on port 5432?

Nothing is listening where you pointed. Three possibilities, in order of likelihood: the database is
not running; it is running but bound to a different address; or a firewall is dropping the
connection.

sudo systemctl status postgresql
sudo ss -tlnp | grep 5432

If it listens on 127.0.0.1 and you are connecting from another machine, that is your answer —
either connect over a private network or change listen_addresses. Do not expose the port publicly
to solve this.

password authentication failed

Credentials are wrong, or the authentication method is not what you assume. PostgreSQL decides how
to authenticate based on where the connection comes from, so the same credentials can succeed
locally and fail over TCP.

Check pg_hba.conf. A line using peer authentication matches the operating system user, not the
password — which is why connecting as a different system account fails with a password that is
perfectly correct.

database does not exist

Usually a typo, or an application pointed at the wrong environment. Occasionally it means the
restore you thought succeeded did not.

psql -U appuser -l

too many connections

FATAL: sorry, too many clients already

This one is architectural rather than a mistake. PostgreSQL has a connection limit, and every worker
process in your application server holds its own connection — possibly several. Multiply workers by
processes by any background workers and the number grows faster than people expect.

Django's CONN_MAX_AGE keeps connections open between requests, which improves latency and
increases the number held simultaneously. Setting it high without checking the limit is a common way
to produce this error under load.

The real fix at scale is a connection pooler in front of the database, so hundreds of application
connections map onto a small number of real ones.

SSL is required

Managed databases usually insist on encrypted connections, and the default is often to try without.

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": env("DB_NAME"),
        "OPTIONS": {"sslmode": "require"},
    }
}

Use verify-full where you can, with the provider's root certificate — require encrypts the
connection but does not verify who you are talking to.

Errors that only appear under load

Some connection failures are intermittent: a connection idle in the pool is closed by the database
or by something between the two, and the next query fails on a socket that looks open.

The symptom is a small number of errors that never reproduce in testing. A pooler handles it
properly. As a stopgap, lowering CONN_MAX_AGE below the idle timeout of whatever is closing them
makes the problem go away.

A diagnostic order that works

Confirm the database is running. Confirm it listens where you are connecting. Try connecting with
psql using the exact credentials from your settings — if that fails, the problem is not Django.
Only then look at your application configuration.

That sequence turns most of these into a two-minute diagnosis, because it separates "the database is
unreachable" from "the database refused me".


For sizing, pooling and read replicas, see Scaling Django
Databases
.

Comments (0)

Please login to leave a comment.

No comments yet. Be the first to comment!