The “too many connections” error in PostgreSQL means the server has hit its configured connection limit and is refusing new clients. It’s rarely caused by genuinely needing more simultaneous users than the default allows — far more often, it’s a sign that connections aren’t being closed properly somewhere in your application.
The Problem
New connection attempts to PostgreSQL start failing with:
FATAL: too many connections for role "app_user"
FATAL: sorry, too many clients already
Your application may have been working fine for hours or days before this suddenly starts happening, often under moderate load rather than an obvious traffic spike, which makes it feel unpredictable.
Why It Happens
PostgreSQL has a hard limit on simultaneous connections, controlled by max_connections in postgresql.conf (commonly 100 by default). The error appears when that ceiling is reached, and the most common underlying causes are:
- The application isn’t closing database connections after use, so they accumulate over time until the limit is hit — extremely common with ORMs or connection handling code that opens a connection per request without releasing it back to a pool
- No connection pooling is in place, so every request opens a brand new connection directly, which is expensive and doesn’t scale even before the hard limit is reached
- Multiple application instances (e.g. several containers or serverless function invocations) are each opening their own pool sized for standalone use, and combined they exceed what the database allows
- Long-running or idle-in-transaction queries are holding connections open far longer than expected, blocking them from being reused
The Fix
First, check how many connections are currently open and where they’re coming from:
SELECT count(*), usename, application_name, state
FROM pg_stat_activity
GROUP BY usename, application_name, state
ORDER BY count(*) DESC;
A large number of connections sitting in idle or idle in transaction state is the clearest sign of connections not being released properly, rather than genuine concurrent load.
If you’re not already using a connection pooler, add one — this is the standard fix rather than just raising the connection limit. PgBouncer is the most common choice:
[databases]
mydb = host=localhost port=5432 dbname=mydb
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
With transaction pool mode, many application-level connections can share a much smaller number of actual PostgreSQL connections, since each one is only held for the duration of a single transaction rather than the lifetime of the client.
If your application uses an ORM, make sure its own connection pool is configured with a sane maximum size, and that connections are actually being released (closed or returned to the pool) after each unit of work, rather than left open implicitly:
# Example: SQLAlchemy
engine = create_engine(DATABASE_URL, pool_size=10, max_overflow=5)
Only as a last resort, if you’ve confirmed pooling is correctly configured and connections are being released properly, consider raising max_connections in postgresql.conf — but be aware each additional connection consumes memory, so this isn’t a free fix.
Still Not Working?
If connections keep piling up even with pooling in place, look for long-running or stuck transactions that never commit or roll back. You can terminate a specific problematic connection manually while you investigate the root cause in the application code:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND now() - state_change > interval '10 minutes';
It’s also worth checking whether multiple separate applications or services are all connecting to the same database independently, each with their own pool, without anyone having a full picture of the combined total. This is common in microservice setups where each service was configured in isolation, and no single place tracks how many connections the database is expected to serve across all of them combined. Reviewing pg_stat_activity grouped by application_name, as shown earlier, usually makes this kind of hidden multiplication obvious once you look at it as a whole system rather than one service at a time.
If you’re running PostgreSQL inside a container with a fixed memory limit, also keep in mind that raising max_connections significantly increases baseline memory usage even when those connections are idle, since PostgreSQL allocates memory per connection regardless of whether it’s actively running a query. In memory-constrained environments, fixing the underlying connection leak or adding a pooler is almost always a better long-term solution than simply raising the ceiling.