max_connections
Hard cap on concurrent client connections.
What it does
max_connections sets the maximum number of concurrent client sessions. Each connection is a backend process with its own memory overhead, and high connection counts increase contention — so more is not better. It interacts with work_mem (per-operation memory multiplies by connections) and reserves slots for superusers and background workers.
How to tune it
Most workloads are better served by a connection pooler (e.g. PgBouncer) in front of a modest max_connections (a few hundred) than by a very high limit. Raising it costs memory and can hurt throughput under contention. Changing it needs a restart.
In depth
Why "just raise it" is the wrong instinct
Every connection in PostgreSQL is a separate operating-system process with its own memory and its own slice of shared bookkeeping (locks, snapshots). As the number of active backends climbs past the number of CPU cores, they stop running in parallel and start competing — context-switching, lock contention, and cache thrashing all rise. Throughput can actually fall as you add connections.
On top of that, work_mem is charged per operation per connection, so a high
max_connections multiplies your worst-case memory use.
Use a pooler instead
The standard answer is a connection pooler — PgBouncer is the common choice —
in front of a modest max_connections:
- App opens many cheap connections to the pooler.
- The pooler multiplexes them onto a small number of real PostgreSQL backends (often tens, not thousands).
- PostgreSQL stays in its efficient range; memory and contention stay bounded.
A few hundred max_connections with a pooler beats several thousand without one
almost every time.
Practical notes
- Some slots are reserved (
superuser_reserved_connections,reserved_connections) so admins can still log in when the app saturates the rest. - Background workers (parallel query, replication, extensions) draw from
max_worker_processes, separate frommax_connections. - Changing
max_connectionsrequires a restart, and it sizes some shared memory structures — another reason to set it once, sensibly, rather than chase it upward.
Sizing rule of thumb
Estimate the number of genuinely concurrent, active queries you need (often close to your core count for CPU-bound work, higher for I/O-bound), add headroom and reserved slots, and let a pooler absorb the rest. If you're reaching for tens of thousands, the fix is pooling, not a bigger number.
FAQ
- Should I just set max_connections very high?
- Usually not. Each connection is a process with memory and locking overhead, and work_mem is multiplied per connection. A pooler that keeps a small number of busy backends almost always beats a large max_connections.