max_connections

Maximum concurrent connections. Too high × active sorts: OOM kills all sessions. Too low: connection refused. Use PgBouncer for >200.

HIGH RISK
Safe first move

100 (default). Check: max_connections × work_mem × 2 < total_RAM. Use PgBouncer for >200 app connections.

Main risk

Too high: 300 connections × 4MB work_mem = OOMKilled=true on 768MB box (lab-captured). Too low: FATAL: remaining connection slots reserved for superuser.

Tier 2 — Production Guide — Operational guidance is available; evidence scope is stated where relevant.

What We Observed

box

+ cl_mc_safe (--memory=1g), pgbench TPC-B

connection refused

setting

max_connections=15, superuser_reserved=3

normal user ceiling

12

result

connections 13+ refused: FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute

source file

normal operation

setting

max_connections=100

clients

20

tps

995.15

latency ms

20.097

oomkilled

false

source file

curve note

cliff behavior: below capacity → connection refused; above memory → OOM. No gradual degradation.

What Was Expected

framing

max_connections is NOT a safety valve. When every connection is active and running work_mem sorts, the RAM formula multiplies fast: connections × work_mem × sort_nodes × hash_mem_multiplier. OOM kills all sessions simultaneously.

formula

peak_mem = max_connections × work_mem × avg_sort_nodes × hash_mem_multiplier

example

300 × 4MB × 2 × 2 = 4.8GB → OOM on 768MB container

lab evidence
container

768MB cap (from mc_danger.txt)

max connections

300

result

OOMKilled=true

log

server process (PID N) was terminated by signal 9: Killed

source file

mc danger raw

== box load == 0.75 1.35 1.18 5/1122 293183

== PART A  DANGER: max_connections=300 (no backpressure), 768MB box, 10-client spike ==
  max_connections=300
  waiting for the spike to resolve...

OOMKilled=true Status=running ExitCode=0

  postgres crash-cascade evidence:
    This user must also own the server process.
    2026-06-19 22:11:45.020 UTC [1] LOG:  server process (PID 161) was terminated by signal 9: Killed
    2026-06-19 22:11:45.020 UTC [1] LOG:  terminating any other active server processes
    2026-06-19 22:11:45.114 UTC [1] LOG:  all server processes terminated; reinitializing
    2026-06-19 22:11:45.701 UTC [174] LOG:  database system was not properly shut down; automatic recovery in progress
  per-client view (what the application saw):
          9 WARNING:  terminating connection because of crash of another server process
         10 server closed the connection unexpectedly
== PART B  CONTROL: max_connections=3 (reserved=1 -> 2 usable), SAME 768MB box, SAME 10-client spike ==
  max_connections=3
  waiting...
  instance state:
    OOMKilled=false Status=running
  still serving queries?   YES alive: 2026-06-19 22:12:38.856831+00
  per-client view (recoverable refusals, NOT a crash):
          8 psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  remaining connection slots are reserved for roles with the SUPERUSER attribute

torn down cl_mc_oom DONE

source file

Common Pitfalls

  • Raising max_connections to 'fix' connection errors instead of adding a pooler -- this removes admission backpressure and arms an OOM cascade: under load enough work_mem-hungry backends are admitted to exhaust RAM and crash the whole instance.
  • Forgetting it needs a RESTART (context=postmaster) -- ALTER SYSTEM + reload does nothing live (pending_restart=t); you cannot bump it during an incident, so capacity must be planned ahead.
  • Sizing it to the application's peak connection count rather than to what the hardware can serve -- thousands of backends mostly add context-switch/lock contention and per-backend memory, not throughput.
  • Ignoring the per-backend memory math -- max_connections x (work_mem x nodes x parallel multiplier) can dwarf RAM long before you hit the connection limit; the OOM hits under LOAD, not at idle.
  • Not reserving admin slots -- with superuser_reserved_connections too low (or reserved_connections unset on PG16+) you can be locked out of your own saturated server exactly when you need to connect.
  • Setting an enormous value 'just in case' -- it inflates shared memory and the lock table (sized off MaxBackends) and the proc-array scan cost even when those connections are idle.
  • Assuming autovacuum/replication share the client pool -- they get their own slots on top (MaxBackends), so if you under-size max_wal_senders or max_worker_processes you starve those, not client connections.

Tuning Guidance

Unlock Incident Pro

The free sections tell you what to do. Pro is the measured proof — the exact failure signature, safe-ceiling formula, and per-workload defaults, each captured on real PG 14–18 runs.

  • Tuning Guidance
Unlock Incident Pro

max_connections is the hard cap on how many client backends can be connected to the server at the same time. PostgreSQL uses a process-per-connection model: every connection is a separate OS backend process, and max_connections fixes how many of those can exist concurrently. It is set once at server start (context=postmaster) and reserves fixed-size shared-memory structures (the proc array, lock table, predicate-lock table, etc.) sized for that many backends -- which is why it cannot be changed without a restart. A normal (non-superuser) login can occupy at most max_connections - superuser_reserved_connections (- reserved_connections on PG16+) slots; the remaining slots are held back so an administrator can still get in when the server is saturated. It is deceptively important: set too LOW and your application hits 'sorry, too many clients already' connection storms; set too HIGH and you remove the only admission-control backpressure the server has, letting enough memory-hungry backends in to OOM the whole instance under load.

name

max_connections

setting default

100

unit

(none -- a count of concurrent backends)

vartype

integer

category

Connections and Authentication / Connection Settings

context

postmaster

min val

1

max val

262143

enumvals

(n/a -- integer)

boot val

100

reset val

100

pending restart

false (until changed; ALTER SYSTEM SET max_connections flips pending_restart to true until the server is restarted)

short desc

Sets the maximum number of concurrent connections.

ground truth note

Captured literally from pg_settings on PG 14-18 (-18) on 2026-06-20. setting=100, boot_val=100, reset_val=100 on EVERY version (default_changed=false, boot_fallback_changed=false). context=postmaster (restart required; ALTER SYSTEM + reload does NOT apply it -- proven in the lab: live value stayed 25 and pg_settings.pending_restart became t). vartype=integer, min_val=1, max_val=262143, unit empty. category='Connections and Authentication / Connection Settings', short_desc='Sets the maximum number of concurrent connections.' Default and bounds are identical across 14, 15, 16, 17, 18.

References

  • PostgreSQL docs: Runtime Config -- Connections and Authentication / Connection Settings (max_connections, superuser_reserved_connections, reserved_connections).
  • PostgreSQL docs: Connections and Authentication -- per-role/per-database CONNECTION LIMIT (ALTER ROLE / ALTER DATABASE).
  • PostgreSQL docs: Resource Consumption -- Memory (work_mem, hash_mem_multiplier) and how per-backend memory scales with concurrency.
  • PostgreSQL docs: pg_stat_activity (counting active backends) and pg_settings (pending_restart for postmaster-context parameters).
  • PgBouncer / pgcat documentation: transaction-mode pooling to serve many client connections from a small backend pool.
  • Lab capture 2026-06-20: cl_mc connection-ceiling sweep (ceiling = max_connections - superuser_reserved_connections; restart-required proof) and cl_mc_oom 768MB OOM-cascade vs recoverable-refusal contrast; pg_settings ground truth PG 14-18 (-18).

Technical Reference

Category
Connections and Authentication / Connection Settings
Type
integer ((none; a count -- the maximum number of concurrent client backends the server will allow at once))
Blast radius
cluster
Severity
Critical
Context
postmaster
Versions
14, 15, 16, 17, 18
Reviewed
2026-06-20

Severity scale: 1 Minimal · 2 Low · 3 Moderate · 4 High · 5 Critical.

Keep going

Related & next steps