53300FATALTier 2 — Caution✅ HIGH confidenceremaining connection slots are reserved for non-replication superuser connections
What this means
The connection limit has been reached. Postgres reserves a small number of connection slots exclusively for superusers (controlled by superuser_reserved_connections, default 3). Non-superuser clients receive this error when max_connections minus the reserved slots are all occupied.
Why it happens
- 1Application connection pool misconfiguration: too many connections created relative to max_connections
- 2Connection leak: application code opens connections but does not close them
- 3max_connections is set too low for the workload
- 4Idle connections accumulating (idle_in_transaction sessions not timing out)
How to reproduce
A non-superuser connection attempt when all non-reserved slots are occupied.
-- Check current connection state:
SELECT count(*) AS total,
sum(CASE WHEN state = 'idle' THEN 1 ELSE 0 END) AS idle,
sum(CASE WHEN state = 'idle in transaction' THEN 1 ELSE 0 END) AS idle_in_txn
FROM pg_stat_activity
WHERE pid <> pg_backend_pid();
SHOW max_connections;
SHOW superuser_reserved_connections;Fix 1: Use a connection pooler (PgBouncer)
When many application processes each open their own connections to Postgres.
-- PgBouncer sits between the application and Postgres.
-- Configure pool_size in pgbouncer.ini to limit Postgres connections.
-- Applications connect to PgBouncer (e.g. port 6432) instead of Postgres directly.
-- Verify pooler is working:
SELECT count(*) FROM pg_stat_activity; -- should be low even under high app loadWhy this works
PgBouncer multiplexes many application connections onto a small number of real Postgres backend connections. In transaction pooling mode, a Postgres connection is only held for the duration of a transaction, then returned to the pool. This can reduce required Postgres connections by 10-50x.
Fix 2: Terminate idle connections and tune timeouts
As an immediate relief measure to free connections.
-- Terminate idle connections older than 10 minutes:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND query_start < NOW() - INTERVAL '10 minutes'
AND pid <> pg_backend_pid();
-- Prevent future accumulation:
ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
SELECT pg_reload_conf();Why this works
pg_terminate_backend() sends SIGTERM to idle backends, freeing their connection slots immediately. idle_in_transaction_session_timeout automatically terminates backends stuck in an open transaction after the specified interval, preventing slot leakage.
What not to do
Increase max_connections indefinitely to accommodate all connections
Why it's wrong: Each Postgres connection uses ~5-10MB of shared memory; very high max_connections increases memory pressure and can degrade performance due to lock contention in shared memory.
Version notes
Postgres 14+Connection slot reservation for replication connections is now controlled separately by reserved_connections (for replication workers) in addition to superuser_reserved_connections.Sources
📚 Official docs: https://www.postgresql.org/docs/current/errcodes-appendix.html
📚 Feature docs: https://www.postgresql.org/docs/current/runtime-config-connection.html#GUC-MAX-CONNECTIONS
🔧 Source ref: src/backend/postmaster/postmaster.c — ConnCreate()
📖 Further reading: Connection Configuration
📖 Further reading: PgBouncer
Confidence assessment
✅ HIGH confidence
Well-documented and very common in production. The PgBouncer recommendation is universally accepted best practice. Edge case: in Postgres 14+ the reserved_connections parameter was added for replication worker slots, subtly changing the slot allocation math.
See also
🔗 Related errors
📄 Reference pages