【PostgreSQL】superuser_reserved_connections による一般ユーザーとスーパーユーザーの接続数上限の違いを検証してみた

【PostgreSQL】superuser_reserved_connections による一般ユーザーとスーパーユーザーの接続数上限の違いを検証してみた

PostgreSQL の `superuser_reserved_connections` パラメータについて、実際に一般ユーザーと superuser で接続を埋めて、接続数の制限がどのように機能するのか確認してみました。
2026.08.19

PostgreSQL には superuser_reserved_connections というパラメータがあり、
スーパーユーザーのために一定数の接続スロットが予約されています。

今回は、一般ユーザーと superuser で実際に接続を埋めていき、
max_connections - superuser_reserved_connections の計算通りに
一般ユーザーの接続が制限されるのか、実際に確認してみました。

具体的には以下の内容を検証してみました。

  • 一般ユーザー(test_user)で接続を埋めていき、想定通りの数で制限されるか確認
  • 制限に達した後、superuser(postgres)の場合は、接続ができるか確認

max_connections (integer)
データベースサーバに同時接続する最大数を決定します。
...
superuser_reserved_connections (integer)
PostgreSQLのスーパーユーザによる接続のために予約されている接続「スロット」の数を決定します。...

19.3. 接続と認証

(前提準備) PostgreSQL インストール

検証のための実行環境は EC2(AL2023)にインストールした PostgreSQL を使用しました。
EC2 に PostgreSQL をインストールする手順としては下記をご参照ください。

https://dev.classmethod.jp/articles/installing-postgresql-on-ec2-al2023/

PostgreSQL 17.10 を利用し、検証していきます。

[ec2-user@ip-xx-xx-xx-xx ~]$ sudo -u postgres psql
psql (17.10)
Type "help" for help.

postgres=# SELECT version();
                                                    version                                                    
---------------------------------------------------------------------------------------------------------------
 PostgreSQL 17.10 on x86_64-amazon-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5), 64-bit
(1 row)

現在の接続ユーザーが superuser か確認

基本的に PostgreSQL をインストールした時点で、デフォルトユーザーとして postgres が作られますがそのユーザーが superuser かどうか念の為確認します。

以下の通り、現在は ユーザー postgres で接続しています。

postgres=# SELECT current_user, session_user;
 current_user | session_user 
--------------+--------------
 postgres     | postgres
(1 row)

ロール一覧を確認。ロール(ユーザー) postgres には Superuser 権限が付与されています。

postgres=# \du
                             List of roles
 Role name |                         Attributes                         
-----------+------------------------------------------------------------
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS

\du[S+] [ pattern ]
データベースロールを一覧表示します。 (「ユーザ」と「グループ」という概念は「ロール」に統合されましたので、このコマンドは\dgと同じものになりました。)

psql

上記の確認方法の他、pg_roles ビューを確認する方法もあります。

-- rolsuper が t(true)であれば superuser
postgres=# SELECT rolname, rolsuper FROM pg_roles WHERE rolname = current_user;
 rolname  | rolsuper 
----------+----------
 postgres | t
(1 row)

52.20. pg_roles
pg_rolesビューはデータベースのロールに関する情報を提供します。
...
rolsuper bool
ロールはスーパーユーザの権限を持っている

52.20. pg_roles

以上より、ユーザー postgres はスーパーユーザー権限を持っていることがわかります。
後ほど作成する一般ユーザー(test_user)でも同様のコマンドを実行し、比較していきます。

max_connections を小さくする

検証をしやすくするため max_connections の値を小さくします。

まずは、現在の max_connections および superuser_reserved_connections の値を確認します。

postgres=# SHOW max_connections;
 max_connections 
-----------------
 100
(1 row)

postgres=# SHOW superuser_reserved_connections;
 superuser_reserved_connections 
--------------------------------
 3
(1 row)

以下記事の「max_connections の変更」の手順を参考に max_connections の値を 5 に変更します。

https://dev.classmethod.jp/articles/postgresql-too-many-clients-already-reproduce/#max_connections-%25E3%2581%25AE%25E5%25A4%2589%25E6%259B%25B4

以下のようになっていれば OK です。(superuser_reserved_connections はそのままです。)

postgres=# SHOW max_connections;
 max_connections 
-----------------
 5
(1 row)

postgres=# SHOW superuser_reserved_connections;
 superuser_reserved_connections 
--------------------------------
 3
(1 row)

検証用の一般ユーザーを作る

検証用に test_user ロール(ユーザー)を作成します。

-- ロール作成
postgres=# CREATE ROLE test_user WITH LOGIN PASSWORD 'testpass123';
CREATE ROLE

作成した test_user の権限も確認しておきます。
下記の通り、rolsuperf のため、本ユーザーは superuser 権限を持たない、ごく一般的なロールであることがわかります。

postgres=# SELECT rolname, rolsuper FROM pg_roles WHERE rolname = 'test_user';
  rolname  | rolsuper 
-----------+----------
 test_user | f
(1 row)

postgres=# \du
                             List of roles
 Role name |                         Attributes                         
-----------+------------------------------------------------------------
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS
 test_user | 

一般ユーザーから接続できるよう pg_hba.conf を修正

作成済みの test_user で PostgreSQL サーバーへ接続自体ができるか確認します。

現在、接続を試行すると下記の通りエラーとなります。

$ psql -U test_user -d postgres
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  Peer authentication failed for user "test_user"

上記エラーの理由は、local(Unixソケット)接続では peer 認証方式が使われているためです。(pg_hba.conf にて設定されるパラメータ)
peer 設定では OS ユーザー名と PostgreSQL のロール名が一致するかを認証します。
今回は EC2 環境を用いているので EC2 の OS ユーザー ec2-user から test_user としてログインしようとしたため、認証エラーとなりました。

peer
クライアントのオペレーティングシステムにおけるユーザ名をオペレーティングシステムから取得し、ユーザ名が要求されたデータベースユーザ名と一致するか検査します。 これはローカル接続の時にのみ使用可能です。

20.1. pg_hba.confファイル

そのため、pg_hba.conf を編集し、認証方式をパスワード認証が必要な scram-sha-256 に変更します。

scram-sha-256
ユーザのパスワードを検証するためにSCRAM-SHA-256認証を実行します。

20.1. pg_hba.confファイル

scram-sha-256 方式では、ログインの際にパスワードが必須となるため、デフォルトユーザー postgres のパスワードも予め設定しておきましょう。

-- postgres ユーザーでログインし、
-- 本ユーザーの接続用に任意のパスワードを設定しておく
postgres=# ALTER ROLE postgres WITH PASSWORD 'your_strong_password';
ALTER ROLE

上記にてパスワード変更ができたら、pg_hba.conf を編集します。
vim エディタで設定ファイルを開きます。

$ sudo vi /var/lib/pgsql/data/pg_hba.conf

下記のように scram-sha-256 を使用するよう変更します。

# 変更前
local   all             all                                     peer

# 変更後
local   all             all                                     scram-sha-256

変更を反映させるため PostgreSQL をリロードします。

$ sudo systemctl reload postgresql

再度一般ユーザー test_user で試してみると、パスワードが求められ、
ユーザー作成時に指定したパスワードを入力すると、接続ができました。

$ psql -U test_user -d postgres
Password for user test_user: 
psql (17.10)
Type "help" for help.
postgres=> 

もちろん、デフォルトユーザー postgres で試しても、
パスワード入力が求められ、接続ができました。

$ sudo -u postgres psql
Password for user postgres: 
psql (17.10)
Type "help" for help.

postgres=# 

一般ユーザーで接続を埋める

※本検証を始める際は superuser 権限を持つ postgres ユーザーからの接続は全て切断しておいてください。以下のように test_user からの接続のみになっていれば OK です。

ターミナルA
postgres=> SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity WHERE backend_type = 'client backend';
  pid   |  usename  | state  |  backend_type  |          query_start          
--------+-----------+--------+----------------+-------------------------------
 104731 | test_user | active | client backend | 2026-08-15 11:15:56.611448+00
(1 row)

現在は max_connections=5, superuser_reserved_connections=3 です。
そして現在 PostgreSQL に接続しているのは本クエリを実行している自分(test_user)の 1 接続のみです。

ターミナルA
postgres=# SHOW max_connections;
 max_connections 
-----------------
 5
(1 row)

postgres=# SHOW superuser_reserved_connections;
 superuser_reserved_connections 
--------------------------------
 3
(1 row)

-- 現在接続しているクライアントを確認
postgres=> SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity WHERE backend_type = 'client backend';
  pid   |  usename  | state  |  backend_type  |          query_start          
--------+-----------+--------+----------------+-------------------------------
 104731 | test_user | active | client backend | 2026-08-15 11:15:56.611448+00
(1 row)

公式ドキュメントを確認すると、以下の通り、最大接続数は max_connections であり、その内の superuser_reserved_connections がスーパーユーザーのために予約された接続数と読み取れます。

max_connections (integer)
データベースサーバに同時接続する最大数を決定します。
...
superuser_reserved_connections (integer)
PostgreSQLのスーパーユーザによる接続のために予約されている接続「スロット」の数を決定します。

19.3. 接続と認証

すなわち、一般ユーザーである test_user から接続できる数は下記の計算式になるはずです。

  • 一般ユーザーの枠 = max_connections - superuser_reserved_connections

今回の場合は 5 - 3 = 2 です。本当にそうなるか確認してみましょう。
ターミナル B を開き、test_user で接続を試行します。

ターミナルB
$ PGPASSWORD='testpass123' psql -U test_user -d postgres -c "SELECT pg_sleep(300);" &
[1] 104896

ターミナル A から確認すると先ほどと比較し、接続が増えていることがわかります。

ターミナルA
postgres=> SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity WHERE backend_type = 'client backend';
  pid   |  usename  | state  |  backend_type  |          query_start          
--------+-----------+--------+----------------+-------------------------------
 104731 | test_user | active | client backend | 2026-08-15 11:21:46.584027+00
 104897 | test_user | active | client backend | 2026-08-15 11:20:38.093783+00
(2 rows)

それではターミナル B から同じ test_user でもう1接続追加してみます。(3接続目)
その結果、今度は失敗しました。

ターミナルB
$ PGPASSWORD='testpass123' psql -U test_user -d postgres -c "SELECT pg_sleep(300);" &
[2] 104956
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

もちろん、ターミナル A の pg_stat_activity の内容も変化はありません。

ターミナルA
postgres=> SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity WHERE backend_type = 'client backend';
  pid   |  usename  | state  |  backend_type  |          query_start          
--------+-----------+--------+----------------+-------------------------------
 104731 | test_user | active | client backend | 2026-08-15 11:23:15.512422+00
 104897 | test_user | active | client backend | 2026-08-15 11:20:38.093783+00
(2 rows)

では続いて ターミナル B から superuser 権限を持つ postgres で接続してみましょう。
下記の通り無事入れました。

ターミナルB
$ sudo -u postgres psql
Password for user postgres: 
psql (17.10)
Type "help" for help.

postgres=# 

一方で、ターミナル A から接続が増えたかどうか確認しますが、接続は増えていないように見えます。

ターミナルA
postgres=> SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity WHERE backend_type = 'client backend';
  pid   |  usename  | state  |  backend_type  |          query_start          
--------+-----------+--------+----------------+-------------------------------
 104731 | test_user | active | client backend | 2026-08-15 11:25:37.473418+00
 104897 | test_user | active | client backend | 2026-08-15 11:20:38.093783+00
(2 rows)

上記のように postgres の接続が見えないのは、WHERE 句条件を付けているためです。下記の通り外すと pid=105017 の postgres 接続が増えていることがわかります。
(一般ユーザー test_user で pg_stat_activity を見ると、自分以外のユーザーの backend_type や state が空欄になっていました。権限による表示制限があるようです)

ターミナルA
postgres=> SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity;
pid   |  usename  | state  |  backend_type  |          query_start

--------+-----------+--------+----------------+-------------------------------
105017 | postgres  |        |                |
104731 | test_user | active | client backend | 2026-08-15 11:31:28.160154+00
105189 | test_user | active | client backend | 2026-08-15 11:30:42.105777+00
2729 |           |        |                |
2730 | postgres  |        |                |
2725 |           |        |                |
2726 |           |        |                |
2728 |           |        |                |
(8 rows)

ちなみに superuser の postgres で接続したターミナル B から見ると、postgres の接続が増えていることがわかります。

ターミナルB
postgres=# SELECT pid, usename, state, backend_type, query_start FROM pg_stat_activity WHERE backend_type = 'client backend';
  pid   |  usename  | state  |  backend_type  |          query_start          
--------+-----------+--------+----------------+-------------------------------
 104731 | test_user | idle   | client backend | 2026-08-15 11:25:16.354961+00
 104897 | test_user | active | client backend | 2026-08-15 11:20:38.093783+00
 105017 | postgres  | active | client backend | 2026-08-15 11:25:32.481231+00
(3 rows)

予想通り、max_connections=5, superuser_reserved_connections=3 の状態の場合、
一般ユーザーからの接続は superuser_reserved_connections を差し引いた 5 - 3 = 2 接続しかできませんが、superuser権限を持つユーザーであればちゃんと接続ができていることがわかりました。

終わりに

今回の検証で、以下のことが確認できました。

  • サーバーへの接続数は max_connections によって上限が定められている
  • superuser_reserved_connections により、予めスーパーユーザー用の接続が予約されている。
  • そのため、一般ユーザーが使える接続数は max_connections - superuser_reserved_connections となる

superuser_reserved_connections があることで、何らかの理由で一般ユーザーからの接続が急増してトラブルになった場合でも、スーパーユーザーで接続して調査することができます。
本パラメータのように、データベース技術は障害対応まで見据えた設計になっていることに気づき、勉強になりました。

本記事がどなたかのお役に立てば幸いです。

参考情報

https://www.postgresql.jp/document/17/html/runtime-config-connection.html
https://dev.classmethod.jp/articles/installing-postgresql-on-ec2-al2023/
https://www.postgresql.jp/document/17/html/app-psql.html
https://www.postgresql.jp/document/17/html/view-pg-roles.html
https://dev.classmethod.jp/articles/postgresql-too-many-clients-already-reproduce/#max_connections-%25E3%2581%25AE%25E5%25A4%2589%25E6%259B%25B4
https://www.postgresql.jp/document/17/html/auth-pg-hba-conf.html

この記事をシェアする

関連記事