pgconfigurator
pgconfigurator

max_connections

Requires a restarttuned by pgconfiguratorcount (concurrent connections)

Hard cap on concurrent client connections.

What pgconfigurator would set
32 GB · 8 vCPU · NVMe · OLTP
Computing…
Tune for your exact server → /tunecomputed in your browser · nothing uploaded

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 from max_connections.
  • Changing max_connections requires 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.

Related parameters