You're mid-migration or maybe just trying to run a quick query, and bam—FATAL: sorry, too many clients already. Error code 53300. I've seen this kill production apps at 2 AM more times than I can count. Last month, a client's POS system ground to a halt because their inventory app kept opening connections and never closing them. The fix wasn't as simple as bumping a number.
Here's the thing: this error means PostgreSQL's max_connections setting is hit. But why it's hit matters more than the error itself. Let's go through the three most common causes, starting with the one that's easiest to fix.
Cause 1: max_connections is just too low for your workload
This is the most common scenario. You've got a small server, default settings, and suddenly you have a few apps plus some monitoring tools all connecting. Default max_connections is 100. That sounds like a lot, but each connection takes about 5-10 MB of RAM. And if you're running multiple apps—say, a web app, a cron job, and a BI tool—you can blow through 100 fast.
The fix: raise max_connections (but not blindly)
First, check your current setting:
SHOW max_connections;
If it's 100 and you're hitting the limit, you can raise it. But here's the catch: every connection eats memory. On a server with 4 GB RAM, going to 500 connections might swap your system to death. I always calculate it roughly: each connection uses about 2-5 MB (depends on your work_mem and other settings). So if you have 8 GB RAM, 200-300 connections is usually safe.
Edit postgresql.conf (usually in /etc/postgresql/16/main/ on Debian, or /var/lib/pgsql/16/data/ on RHEL):
max_connections = 200
Then restart PostgreSQL (or reload if you set it via ALTER SYSTEM):
sudo systemctl restart postgresql
But honestly, if you're hitting 100 with normal traffic, you might have a connection leak. Don't just crank it up—you'll hide the problem. The real fix is often Cause 2.
Cause 2: Application connection leaks (the silent killer)
This is the one that makes me angry. You've got an app that opens a connection, runs a query, and forgets to close it. Over time, connections pile up. I had a client whose Node.js app had a pool size of 50, but because they never called pool.end() in error paths, after a few days every connection was stuck. The server wasn't overloaded—it just had 100 zombie connections.
How to spot a leak
Run this query to see who's connected and for how long:
SELECT pid, usename, application_name, client_addr, state, now() - state_change AS idle_time
FROM pg_stat_activity
WHERE state = 'idle'
ORDER BY idle_time DESC;
If you see dozens of idle connections that have been idle for hours, you've got a leak. Especially if they're from the same application_name.
The fix: find and fix the leak
First, kill the zombies to get back online:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle' AND now() - state_change > INTERVAL '5 minutes';
Then go fix your code. In most languages, use a connection pool with a timeout. For example, in Python with psycopg2:
from psycopg2 import pool
conn_pool = pool.SimpleConnectionPool(1, 20, dbname='mydb')
# Always return the connection
conn = conn_pool.getconn()
try:
cur = conn.cursor()
cur.execute('SELECT 1')
finally:
conn_pool.putconn(conn)
The key is the finally block. If you're using an ORM like SQLAlchemy, set pool_pre_ping=True and a reasonable pool_recycle.
Also, check for long-running transactions. A transaction that never commits holds a connection. Use SELECT * FROM pg_stat_activity WHERE xact_start IS NOT NULL to find them.
Cause 3: Too many connections from a single client or middleware
Sometimes it's not a leak—it's just that your app or a tool like a reporting dashboard opens way too many connections. I saw a Tableau server that opened 50 connections per user. With 10 users, that's 500 connections. The database wasn't the problem; the client was.
The fix: use a connection pooler like PgBouncer
The real solution for high connection counts is to put a pooler in front of PostgreSQL. PgBouncer is the go-to. It multiplexes thousands of client connections into a small pool (say, 20) that actually hits the database. Your app thinks it's talking to Postgres directly, but PgBouncer is sitting in the middle.
Install it (on Ubuntu):
sudo apt install pgbouncer
Configure /etc/pgbouncer/pgbouncer.ini:
[databases]
mydb = host=127.0.0.1 port=5432
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
default_pool_size = 20
max_client_conn = 1000
Then point your app to port 6432 instead of 5432. This is the cleanest way to handle 500+ connections without killing your database server.
Alternatively, if you can't install PgBouncer, you can lower idle_in_transaction_session_timeout in PostgreSQL to force connections to die if they sit idle too long. But that's a band-aid.
Quick-reference summary
| Cause | Symptom | Fix |
|---|---|---|
| max_connections too low | Error even with low traffic, SHOW max_connections under 200 | Increase max_connections in postgresql.conf, restart |
| Connection leak | Many idle connections in pg_stat_activity | Terminate idle, fix app code to close connections |
| Too many clients per app | High connection count from one source | Install PgBouncer, set pool size |
If you're getting this error repeatedly, don't just google and copy a setting. Look at your actual connection usage first. The query I gave above will tell you everything you need. And if you're on a managed service like RDS, you can't change max_connections directly—you'll need to use a pooler or reduce your app's connections.
One last thing: always set connection_limit in your connection strings if your app framework supports it. Things like psycopg2 let you cap the pool. It's one less thing to panic about later.