remaining connection slots are reserved for non-replication superuser connections
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.
- 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)
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;expected output
FATAL: remaining connection slots are reserved for non-replication superuser connections
Fix 1
Use a connection pooler (PgBouncer)
WHEN 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 load
Why 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
WHEN 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.
✕ Increase max_connections indefinitely to accommodate all connections
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.
Connection slot reservation for replication connections is now controlled separately by reserved_connections (for replication workers) in addition to superuser_reserved_connections.
src/backend/postmaster/postmaster.c — ConnCreate()
Connection Configuration ↗PgBouncer ↗Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev