Home / Tutorials / PostgreSQL

PostgreSQL · Database · Production down

Fix: FATAL: sorry, too many clients already

Error FATAL: sorry, too many clients already

By the Erzon engineering team · A step-by-step fix, not a forum thread

Your application logs are filling with FATAL: sorry, too many clients already, new requests that touch the database are failing, and healthy connections are being refused. The server itself is up, but it has run out of connection slots.

What this error means

PostgreSQL allows a fixed number of concurrent connections, set by max_connections. When every non-reserved slot is in use, the server rejects any further connection attempt with this FATAL message before the client can even authenticate. It is not a query error or a crash. The database is working, but it has no free slot to hand you.

Common causes

  • An application or ORM connection pool sized larger than max_connections, or many app instances each opening their own pool that sum past the limit.
  • Connection leaks in application code: connections checked out and never returned because of missing close() calls or unhandled exceptions.
  • idle in transaction sessions that opened a BEGIN and never committed or rolled back, holding their slot (and often locks) indefinitely.
  • No connection pooler in front of Postgres, so short lived requests each open a fresh backend instead of reusing a warm one.
  • A traffic spike, retry storm, or job backlog that briefly needs more concurrency than the server is configured for.
  • max_connections set low relative to real demand, or reduced by a managed service tier (RDS and Aurora derive a default from instance memory).

How to fix it

Work in order: get service back first, then find who is holding the slots, then fix the underlying cause.

  1. Get in through the reserved superuser slot. Regular roles are blocked, but superuser_reserved_connections (default 3) keeps slots open for superusers. Connect as a superuser role.
psql -h your-host -U postgres -d your_database
  1. See how full you are and what the ceiling is.
SHOW max_connections;
SELECT count(*) AS total,
       count(*) FILTER (WHERE state = 'idle in transaction') AS idle_in_txn,
       count(*) FILTER (WHERE state = 'idle') AS idle
FROM pg_stat_activity;
  1. Identify the offenders. Look for sessions that are idle in transaction or have been idle a long time, grouped so you can see which application is responsible.
SELECT pid, usename, application_name, client_addr, state,
       now() - state_change AS idle_for,
       left(query, 60) AS last_query
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'idle')
ORDER BY state_change;
  1. Terminate the stuck sessions to free slots immediately. pg_terminate_backend closes the connection. Never terminate your own admin session or the internal background workers. Target the specific offenders.
-- Cancel any running statement first (gentler), then terminate if needed
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND now() - state_change > interval '5 minutes'
  AND pid <> pg_backend_pid();
  1. Confirm slots have freed and normal traffic can connect again.
SELECT count(*) FROM pg_stat_activity;
  1. Now diagnose the real cause before it refills. If it is an app pool leak, restart or scale the offending service and check its pool configuration. If sessions keep going idle in transaction, set a timeout so the server self heals.
-- Self-managed: set globally then reload (no restart needed)
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';
SELECT pg_reload_conf();
  1. Fix the architecture. If you have no pooler, put PgBouncer in front of Postgres in transaction mode so hundreds of client connections share a small number of server backends. On RDS or Aurora, use RDS Proxy, and change limits through the DB parameter group (a static parameter change requires a reboot), not postgresql.conf.
# pgbouncer.ini (excerpt)
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20

How to prevent it

  • Put a connection pooler (PgBouncer, or RDS Proxy on AWS) between the app and the database, and keep total server backends well under max_connections.
  • Size every application pool deliberately: sum of all app instance pool sizes plus admin headroom must stay below max_connections.
  • Set idle_in_transaction_session_timeout and a sane statement_timeout so stuck sessions cannot camp on a slot.
  • Ensure code returns connections in a finally block or uses the framework context manager, so exceptions do not leak connections.
  • Alert on pg_stat_activity count crossing roughly 80 percent of max_connections, and on any idle in transaction older than a minute.
  • Only raise max_connections after confirming memory headroom, since each connection consumes memory (work_mem per sort, plus per backend overhead).

When to call a senior engineer

If terminating idle sessions frees slots but they refill within minutes, or you are tempted to raise max_connections on a memory constrained box under live load, stop. At that point you are guessing at a leak in application code or a pooler misconfiguration, and a wrong change (raising the limit into an out of memory event, or terminating the wrong backend mid-write) can turn a recoverable outage into data loss or a full crash. That is the moment to bring in someone who can trace the leak to its source and size the pool safely while the system stays up.

Related errors we fix

Still down after this?

Want a senior engineer to just fix it?

Paste this exact error into the form. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.

Book a fix

← Back to all error fixes

Still down?

Get a senior engineer on it now

Leave your email and a line about what is happening. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.

Or email [email protected]. Same engineers, same one-hour clock.