Part of Managed PostgresPostgres · Connections

Postgres “too many connections”: what is holding them, and how to size the pool.

Postgres refuses a new connection when its slots are used up, and it keeps the last few for superusers, which is where the “reserved” message comes from. Raising the limit rarely fixes it. Find what holds the connections, cap each process’s pool so the total fits, and add a pooler only when many processes share one database.

Facts checked

Which message you got, and what is full

Two different things get called “too many connections”. Either the database server ran out of slots, or your app’s own pool ran out of connections and a request gave up waiting. The fixes are different, so read the exact text first.

MessageWho says itWhat is full
FATAL: sorry, too many clients already †PostgresEvery slot up to max_connections (default 100) is taken.
remaining connection slots are reserved for non-replication superuser connections † †Postgres 15 and olderOnly the superuser_reserved_connections slots (default 3) are left, and your app is not a superuser.
remaining connection slots are reserved for roles with the SUPERUSER attribute † †Postgres 16 and newerSame as above, new wording.
remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role † †Postgres 16 and newer, when reserved_connections is above 0 (default 0)The extra reserved slots are all that is left.
ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5.000 seconds †RailsThe Rails pool, not the server. More threads want a connection than pool allows.
P2024: Timed out fetching a new connection from the connection pool †Prisma 6 and olderPrisma’s pool. See Prisma P2024.
timeout exceeded when trying to connect †node-postgres (pg)The pg.Pool, once you set connectionTimeoutMillis (by default it waits forever).
Client disconnected after waiting †PgBouncerNo server connection came free within query_wait_timeout (default 120 seconds).

† Competitor details come from each vendor’s own published pages on the dates shown; prices and plans change, so check the vendor before you decide. `FATAL: sorry, too many clients already` (2026-10-29) · `remaining connection slots are reserved for non-replication superuser connections` (2026-10-29) · `remaining connection slots are reserved for non-replication superuser connections` (2026-10-29) · `remaining connection slots are reserved for roles with the SUPERUSER attribute` (2026-10-29) · `remaining connection slots are reserved for roles with the SUPERUSER attribute` (2026-10-29) · `remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role` (2026-10-29) · `remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role` (2026-10-29) · `ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5.000 seconds` (2026-10-29) · `P2024: Timed out fetching a new connection from the connection pool` (2026-10-29) · `timeout exceeded when trying to connect` (2026-10-29) · Client disconnected after waiting (2026-10-29)

Find what is holding the connections

Connect with psql and look before you change anything. pg_stat_activity has one row per connection. A row whose state is idle is a connection sitting in some pool, doing nothing. idle in transaction is worse: a transaction was opened and never committed, and it also holds locks.

  • Many `idle` rows from one `application_name`: a pool sized too big, or a process that creates a new pool per request. Cap the pool (below) and create it once at startup.
  • Rows from processes you forgot: an old deploy, a preview, a worker, a notebook, a BI tool left open. Each one keeps its own pool.
  • `idle in transaction` for minutes: a code path that opens a transaction and returns early, or a debugger stopped mid-request. Fix the code, and set idle_in_transaction_session_timeout so Postgres ends them for you.
  • Lots of `active` rows, all slow: the connections are busy, not leaked. More connections will not help; slow queries will. See find slow queries.
-- Who holds connections to this database, and doing what
SELECT usename, application_name, state, count(*) AS conns,
       max(now() - state_change) AS longest_in_state
FROM pg_stat_activity
WHERE datname = current_database()
  AND backend_type = 'client backend'
GROUP BY 1, 2, 3
ORDER BY conns DESC;

-- Sessions stuck inside an open transaction
SELECT pid, usename, application_name, now() - state_change AS stuck_for, left(query, 80) AS last_query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY stuck_for DESC;

The pool arithmetic for a small app

Every process that talks to the database keeps its own pool, and every connection in it can be open at the same time. The number that has to fit under the server’s limit is the sum over every process:

(web processes × connections per process) + (job processes × connections per process) + release command or migrations + anything else that connects (previews, `psql`, BI tools)

A connection per thread is enough. A Puma process with 3 threads never needs more than 3 connections for requests. Give it more and they sit idle. Give it fewer and threads queue for one, which is the Rails timeout above.

# Rails (config/database.yml). Rails 8.1 also accepts max_connections:
production:
  url: <%= ENV["DATABASE_URL"] %>
  pool: <%= ENV.fetch("DB_POOL", 6) %>   # 3 Puma threads + 3 job threads

// Node (pg): one Pool per process, created once at startup
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 8 });

# Django: persistent connections, one per worker thread
DATABASES["default"]["CONN_MAX_AGE"] = 600

# SQLAlchemy: cap the overflow too, or 5 becomes 15
engine = create_engine(url, pool_size=5, max_overflow=2)
StackWhat sets connections per processDefault
Rails + Puma † † †pool in database.yml (renamed max_connections in Rails 8.1; pool still works)Generated config: RAILS_MAX_THREADS, or 5. New apps’ puma.rb runs 3 threads and 1 worker.
Django †One connection per worker thread. CONN_MAX_AGE keeps it open; Django 5.1+ can pool with "pool": True (psycopg 3)CONN_MAX_AGE = 0: closed after each request
SQLAlchemy †pool_size + max_overflow5 + 10, so up to 15 per process; waits 30 s
Node (pg) †max on pg.Pool10; no acquire timeout
Prisma † †v6: connection_limit in the URL. v7: the driver adapter’s pool (max for pg)v6: physical CPUs × 2 + 1. v7 with pg: 10
Canner databaseCap per databaseUp to 20 connections open at once

† Competitor details come from each vendor’s own published pages on the dates shown; prices and plans change, so check the vendor before you decide. Rails + Puma (2026-10-29) · Rails + Puma (2026-10-29) · Rails + Puma (2026-10-29) · Django (2026-10-29) · SQLAlchemy (2026-10-29) · Node (`pg`) (2026-10-29) · Prisma (2026-10-29) · Prisma (2026-10-29)

Worked example: fitting under 20

A Canner database accepts up to 20 connections at once (background jobs docs). A project runs one process, so the budget is easy to lay out. A separate Worker project connected to the same database counts against the same 20:

  • Rails with GoodJob in the web process: 3 Puma threads + 3 job threads + 3 for GoodJob’s listener and housekeeping = 9. The docs’ example sets DB_POOL to 12, which leaves 8.
  • Node with one `pg.Pool`: the default max of 10 uses half the budget. A preview deploy of the same app is another process with the same DATABASE_URL and another 10. Set max: 8 and both fit, with room for psql.
  • Prisma 6: the default depends on the CPU count of the machine the process sees, not on your plan. On a host with 8 physical cores that is 17. Set connection_limit yourself.
  • The release command: migrations run while the current version is still serving, so they need a connection or two of headroom.

Why raising max_connections is the last resort

Each Postgres connection is a separate server process with its own memory. Past the point where the CPU and disk are busy, more connections only add contention. The PostgreSQL wiki’s rule of thumb for active connections is (core_count × 2) + effective_spindle_count, a much smaller number than most people expect.

max_connections also needs a server restart. On a managed database you usually can’t set it at all; the provider sizes it from memory. If you are at the limit because of idle connections, smaller pools fix it for free.

PgBouncer: session mode or transaction mode

A pooler sits between your app and Postgres and hands out server connections. What it can share depends on the mode. In session mode (PgBouncer’s default) a client keeps one server connection until it disconnects. Nothing breaks, and nothing is multiplexed: 20 open clients still need 20 server connections. In transaction mode the server connection goes back to the pool at the end of each transaction, so hundreds of mostly idle clients can share a few server connections. The price is that anything tied to a session stops working.

  • Use transaction mode when many short-lived or serverless processes each open connections. Then turn off what depends on a session: DISABLE_SERVER_SIDE_CURSORS = True in Django, pgbouncer=true on Prisma’s pooled URL with a direct URL for migrations, and advisory_locks: false for Rails migrations.
  • Stay on session mode for one or a few long-running processes, and for anything built on LISTEN/NOTIFY or advisory locks: GoodJob, migration locks, Postgres-backed queues.
FeatureSession modeTransaction mode
SET / RESET †YesNever
LISTEN †YesNever (NOTIFY works)
Session-level advisory locks †YesNever
SQL PREPARE / DEALLOCATE †YesNever (protocol-level prepared statements work with max_prepared_statements set)
WITH HOLD cursors, LOAD †YesNever

† Competitor details come from each vendor’s own published pages on the dates shown; prices and plans change, so check the vendor before you decide. `SET` / `RESET` (2026-10-29) · `LISTEN` (2026-10-29) · Session-level advisory locks (2026-10-29) · SQL `PREPARE` / `DEALLOCATE` (2026-10-29) · `WITH HOLD` cursors, `LOAD` (2026-10-29)

When this cap is the wrong fit

Canner runs one process per project and allows up to 20 connections per database, shared with any separate Worker project connected to it. That suits a web app with its jobs in the same process. If your design needs dozens of separate instances or serverless functions talking to one database at the same time, you need a transaction-mode pooler in front of a server sized for it, and Canner does not offer that today.

Questions

What is the difference between “sorry, too many clients already” and “remaining connection slots are reserved”?

Both mean the server is out of slots for you. “Reserved” appears first, when only the slots kept for superusers (3 by default) are left. “Sorry, too many clients already” means every slot up to max_connections is taken.

Should I just raise max_connections?

Usually not. Most “too many connections” incidents are idle connections in oversized pools, or processes nobody remembered. Each connection costs memory, and past the point where the CPU is busy more of them make things slower. Shrink the pools first.

How many database connections does a Rails app with Puma need?

One per thread that can touch the database: Puma threads × Puma workers, plus job threads if jobs run in the same process, plus a few for the job library’s own listener. New Rails apps run 3 Puma threads and 1 worker.

Does PgBouncer fix too many connections?

In transaction mode, yes, by letting many clients share few server connections, at the cost of session features like advisory locks and LISTEN. In session mode it does not reduce the count; each client still holds a server connection.

How many connections does a Canner database allow?

Up to 20 open at once per database, each with its own Postgres session. Size your pools to fit, counting web threads, job threads, previews and anything else you connect.

A Postgres database with every project.

Session connections, so advisory locks and LISTEN work. Compare plans on pricing.