postgresseverity: workaround
too many connections

PostgreSQL FATAL: sorry, too many clients already

sorry, too many clients already

87% fixable~15 mindifficulty: intermediate

Verified against PostgreSQL 17 docs (runtime-config-connection), pgbouncer docs (faq.html), Cloud provider best practices · Updated April 2026

> quick_fix

Your app is opening more connections than Postgres' max_connections setting allows (default 100). Install pgbouncer in front of Postgres to pool connections — this is the industry-standard fix, not raising max_connections.

# Check current setting
psql -c 'SHOW max_connections;'

# Quick raise (requires restart) — NOT recommended for real fix
# Edit postgresql.conf:
# max_connections = 500

# Real fix: use pgbouncer
sudo apt install pgbouncer
# Configure /etc/pgbouncer/pgbouncer.ini and /etc/pgbouncer/userlist.txt
# App connects to pgbouncer (port 6432) instead of Postgres (5432)

What causes this error

Each Postgres connection spawns a backend process using ~10MB of RAM. max_connections caps how many backends can exist at once. When your app connection pool (or long-lived connections from idle clients) exceeds this, Postgres refuses new connections with this FATAL. Raising max_connections is a bandaid; real solution is connection pooling.

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Check current connection count

    See how many connections exist and which databases/users are consuming them.

    SELECT datname, usename, state, COUNT(*)
    FROM pg_stat_activity
    GROUP BY datname, usename, state
    ORDER BY COUNT(*) DESC;
  2. 02

    step 2

    Kill idle connections (temporary)

    Buy yourself breathing room by killing idle-in-transaction connections that have been idle > 10 min.

    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE state = 'idle in transaction'
      AND state_change < now() - interval '10 minutes';
  3. 03

    step 3

    Install and configure pgbouncer

    pgbouncer is a lightweight proxy that pools connections. Your app opens many connections to pgbouncer (cheap), which pools them to a small number of real Postgres connections.

  4. 04

    step 4

    Set transaction-level pooling in pgbouncer.ini

    pool_mode = transaction is the best balance: each transaction gets a connection, then it's returned. Supports most app patterns except prepared statements.

    # /etc/pgbouncer/pgbouncer.ini
    [databases]
    mydb = host=localhost port=5432 dbname=mydb
    
    [pgbouncer]
    pool_mode = transaction
    max_client_conn = 1000
    default_pool_size = 20
  5. 05

    step 5

    Point your app at pgbouncer (port 6432)

    Change DATABASE_URL to connect to pgbouncer port 6432 instead of Postgres 5432. max_client_conn = 1000 means you now support 1000 app connections with just 20 Postgres backends.

Why too many connections happens at the runtime level

PostgreSQL forks one OS process per connection (the postmaster process accepts the TCP connection and forks a backend via fork()). The postmaster enforces max_connections at accept time: if the count of active backends plus reserved superuser slots equals max_connections, the next accept returns the FATAL message before any auth happens. Each backend allocates work_mem-sized buffers per query operation and shares the shared_buffers pool, so 'just raising max_connections' multiplies the fork() cost and the per-backend memory floor. The default 100 is intentionally low, Postgres expects an external pooler.

Common debug mistakes for too many connections

  • Raising max_connections to 1000 in postgresql.conf and skipping pgbouncer, every idle connection now reserves ~10MB of process memory and adds context-switching load, often pushing the host into swap.
  • Setting application connection pool size at 'maxConn / number of app instances', works during steady state but fails in deploys where old and new instances overlap and momentarily double the pool.
  • Using pgbouncer in transaction pooling mode with apps that rely on prepared statements, Postgres 14 and earlier discard prepared statements when a connection is checked back in, so query plans regenerate and you trade one error for performance collapse.
  • Killing idle connections via pg_terminate_backend in a loop, apps see the cancelled connection as a transient error, retry, and the new connection re-fills the pool; root cause untouched.
  • Forgetting that long-running transactions (BEGIN; SELECT FOR UPDATE; ... idle) hold a connection forever, the pool is full of zombies, max_connections is exceeded, and the fix is in app code, not pgbouncer config.

When too many connections signals a deeper problem

Recurring 'too many clients' is almost always an architectural mismatch: a serverless or per-request-spawn application stack (Lambda, AWS Fargate scaling to N tasks, Vercel functions) talking to a single Postgres without a pooler in between. The architectural fix is a connection-pooling layer (pgbouncer in front of self-hosted, or Supavisor/RDS Proxy on managed services) plus a request-scoped pool inside each application instance with a strict timeout. Without it, every traffic spike adds backends linearly and Postgres falls over before the autoscaler even reacts. The fix isn't bigger Postgres; it's a pooler-shaped layer between Postgres and the app fleet.

Editor's take

This error tends to detonate at the single worst moment: your startup just got featured on a product directory, traffic spikes 8x overnight, and suddenly every serverless Vercel function is racing to open its own Postgres connection. At 2am, your on-call rotation discovers max_connections=100 was hit at connection 101, and the entire app is returning 500s. Small teams running a Node.js monolith on a $25 managed Postgres instance feel this hardest — they never hit the ceiling in development because their local Docker instance serves one engineer at a time.

Hitting this error and knowing to reach for pgBouncer rather than just bumping max_connections is a genuine seniority signal. Cranking max_connections to 500 is the junior move — each idle connection still consumes 5-10MB of shared_buffers and spawns a backend process. The engineer who instead installs pgBouncer 1.22+, sets pool_mode=transaction, and wires DATABASE_URL to port 6432 understands that Postgres was designed for dozens of persistent connections, not hundreds of ephemeral ones. That mental model — "the database is not horizontally scalable at the connection layer" — separates engineers who configure infrastructure from those who just use it.

When you're mid-incident on this error, you'll almost certainly see companion failures in the same logs: FATAL: connection to server lost (client timeout racing the pool), FATAL: remaining connection slots are reserved for non-replication superuser connections (the last 3 slots hit), and upstream, your ORM will surface Sequelize ConnectionAcquireTimeoutError or Prisma P2024 — timed out fetching a connection from the pool. Fixing the Postgres ceiling without addressing the ORM's own pool settings (PG_POOL_MAX, DATABASE_CONNECTION_LIMIT) just relocates the bottleneck one layer up.

By Bikram Nath · Curator · Updated April 2026

Frequently asked questions

Why not just raise max_connections to 1000?

Each connection uses ~10MB RAM. 1000 connections = 10GB RAM just for idle Postgres backends. Pgbouncer gives you the same concurrency at a fraction of the RAM.

Does pgbouncer support prepared statements?

Transaction pooling breaks prepared statements because the same connection isn't reused. Use session pooling if you need prepared statements, or use Postgres 17+ which has improved pgbouncer compatibility.

How can I identify which application or query is consuming the most connections?

Query `pg_stat_activity` to see all active connections: `SELECT application_name, client_addr, state, COUNT(*) FROM pg_stat_activity GROUP BY 1, 2, 3 ORDER BY 4 DESC;`. This shows which application names and client IPs hold the most connections and whether they're active, idle, or idle in transaction. Connections in 'idle in transaction' state are often the biggest offenders.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is cross-referenced against the official sources listed in the “sources” sidebar before it ships. If a fix here didn’t work for you, please email so we can update the page.