Skip to main content
RunBook Academy

PostgreSQLIV · Connections, Sessions and PoolingConnections

max_connections, and what it actually reserves

Intermediate⏱ ~25 minpsql

What you'll learn

  • Compute how many slots an ordinary application role actually gets
  • Explain the difference between superuser_reserved_connections and reserved_connections
  • Grant an operator role emergency access without granting superuser
  • Choose a connection ceiling from measured concurrency rather than from pool arithmetic

Prerequisites

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

max_connections is a ceiling on the total, not an allowance for your application. Two reservations are taken out of it before an ordinary role gets a slot, and knowing the arithmetic is what makes the difference between “we have 200 connections available” and the number the application will actually be given.

The arithmetic

slots for ordinary roles
    = max_connections
    - superuser_reserved_connections
    - reserved_connections

Demonstrated on a live cluster deliberately configured small:

Read-only / Safea cluster with a ceiling of 12 and two reservations
$ psql -U postgres -c "SELECT name, setting FROM pg_settings WHERE name IN ('max_connections','superuser_reserved_connections','reserved_connections') ORDER BY name"
              name              | setting
--------------------------------+---------
max_connections                | 12
reserved_connections           | 1
superuser_reserved_connections | 2
(3 rows)

Twelve minus two minus one is nine. Twelve connections were then opened as an ordinary role:

Read-only / Safeexactly nine got in
$ psql -U postgres -tAc "SELECT count(*) FROM pg_stat_activity WHERE usename = 'app'"
9
Read-only / Safethe tenth is refused, and the error names the mechanism
$ psql -h 127.0.0.1 -U app -d postgres -c 'SELECT 1'
psql: error: connection to server at "127.0.0.1", port 5432 failed:
FATAL:  remaining connection slots are reserved for roles with privileges
      of the "pg_use_reserved_connections" role
Read-only / Safeand the superuser still gets in
$ psql -U postgres -tAc "SELECT 'superuser connected: ' || current_user"
superuser connected: postgres

The two reservations, and why there are two

superuser_reserved_connections (default 3) has existed for a long time. It keeps slots for superusers so that an application cannot lock the administrator out of a cluster it has saturated.

reserved_connections (default 0) was added in PostgreSQL 16 and closes a real gap. Before it, the only way to guarantee an operator could connect during an incident was to give them superuser — which is a large privilege grant for a small operational need, and Part V covers why superuser is worth avoiding.

Now you can grant the ability to use reserved slots on its own:

-- A monitoring or on-call role that must be able to connect during an
-- incident, without granting superuser
CREATE ROLE oncall LOGIN PASSWORD 'set-a-real-secret-here';
GRANT pg_monitor TO oncall;
GRANT pg_use_reserved_connections TO oncall;

Then set reserved_connections to a small number — two or three is usually enough — and those slots are unavailable to ordinary application roles.

Choosing the ceiling

The wrong method is to sum the application’s configured pool sizes and add headroom. That produces a number describing what the application is configured to open, which is usually far above what it concurrently uses, and every one of those slots costs memory and a share of the contention discussed in the previous lesson.

The better method starts from measurement:

# Peak concurrency actually observed, by state
psql -U postgres -c \
  "SELECT state, count(*)
     FROM pg_stat_activity WHERE backend_type = 'client backend'
    GROUP BY state ORDER BY count(*) DESC"

# Sample it over time rather than once
psql -U postgres -c \
  "SELECT now(), count(*) FILTER (WHERE state = 'active') AS active,
          count(*) AS total
     FROM pg_stat_activity WHERE backend_type = 'client backend'"

Sample that every ten seconds for a week and take the peak. The ceiling should sit comfortably above the peak total, and the peak active figure is the one that tells you whether you are near the concurrency plateau.

If peak total is far above peak active — which is the usual finding — the honest conclusion is that a pooler will let you lower the ceiling rather than raise it.

Production discipline

  1. Compute the ordinary-role allowance rather than quoting the ceiling. It is max_connections minus both reservations.
  2. Set reserved_connections and grant pg_use_reserved_connections to your on-call role. It defaults to 0 and it is the control that gets you in during a saturation incident without granting superuser.
  3. Size the ceiling from measured peak concurrency, sampled over a week, not from the sum of the application pools.
  4. Read the refusal message carefully. “Too many clients” and “reserved for roles with privileges of” are different states with different remaining options.
  5. Plan max_locks_per_transaction alongside any change to max_connections. They size the same shared structure and both need a restart.

Cross-course references

  • Secrets, PKI & Certificate Management — Part XII (Secret management platforms) covers least-privilege role design, which is the argument for pg_use_reserved_connections over superuser.
  • Observability for Production Sysadmins — Part XX (Alert quality) covers alerting on connection headroom before exhaustion rather than on the exhaustion itself.
  • Linux for Production Sysadmins — Part XXXVII (Resources) covers the file-descriptor and process limits that a high ceiling also consumes on the host.

Quiz

Knowledge check · 6 questions

  1. Q1. A cluster has max_connections 200, superuser_reserved_connections 3 and reserved_connections 0. How many connections can an ordinary application role open?

  2. Q2. What problem does reserved_connections, added in PostgreSQL 16, solve that superuser_reserved_connections did not?

  3. Q3. Which are sound inputs to choosing max_connections? Select all that apply.

  4. Q4. PostgreSQL checks the connection limit before authenticating the client, so a rejected connection costs the server almost nothing.

  5. Q5. Name the two distinct connection-refusal messages PostgreSQL produces and explain why the difference matters during an incident.

  6. Q6. Give the immediate action and the change that prevents a recurrence.

    At 01:50 an application deployment introduces a connection leak. By 02:05 the cluster is refusing connections and the application is failing. The on-call engineer attempts to connect with their normal operator account and is refused. They spend eleven minutes locating superuser credentials in a password vault before they can connect and investigate. The cluster runs PostgreSQL 18 with max_connections 300, superuser_reserved_connections 3, and reserved_connections at its default.

Passing score: 75%. Answers are checked in this browser.