この記事でわかること

  • AlmaLinux 8でPostgreSQLの版を選ぶときの注意(既定のままでは古い版が入る)
  • アプリ専用のユーザーとDBを、必要最小限の権限で作る手順
  • 127.0.0.1からだけ接続させるpg_hba.confの書き方と、つまずきやすいエラーの意味
  • アプリの.envに接続文字列を置き、変更の前にpg_dumpを取る型

アプリのサーバー(GunicornとUvicorn)はFastAPIアプリをGunicornとUvicornで公開する手順で扱いました。この記事は、そのアプリが使うDBの側を扱います。

確かめた環境と、確かめていないこと

ロールやDBの作成、pg_hba.confの動き、アプリからの接続は、2026年10月に作業PCの一時の環境で、PostgreSQL 16.15を127.0.0.1だけで起動して確かめました。アプリ側はSQLAlchemy 2.0.35とpsycopg2 2.9.9です。

一方で、AlmaLinuxでのdnfによるインストールとsystemctlの操作は、RHEL 8の公式ドキュメントの手順に基づいていて、今回の検証では実行していません。また、AlmaLinuxのpostgresql-setup --initdbが作るpg_hba.confの最初の内容も確かめていません。この記事では、その内容に頼らず、必要な行を自分で書く手順にしています。

入れる版を決める(AlmaLinux 8の既定は古い)

PostgreSQLは、各メジャーバージョンを最初のリリースから5年間支援します。2026年10月時点で支援されているのは14〜18で、10は2022年11月に支援が終わっています。

RHEL 8の公式ドキュメントでは、PostgreSQLは9.6・10・12・13・15・16をモジュールのストリームとして選べ、既定のストリームは10です。16はRHEL 8.10から選べます。何も指定せずに入れると、支援の終わった10が入ってしまいます。

【用途】選べる版の確認 /【実行場所】AlmaLinuxのサーバー /【状態変更】なし(読み取り)

dnf module list postgresql

AlmaLinux 8.10のサーバーでは、次のように表示されました(一部)。[d]が既定、[e]が有効になっているストリームです。

postgresql 10 [d][e] client, server [d] PostgreSQL server and client module
postgresql 15        client, server [d] PostgreSQL server and client module
postgresql 16        client, server [d] PostgreSQL server and client module

この記事では、2028年11月まで支援される16を使います。

PostgreSQLをインストールして起動する

この手順は、PostgreSQLがまだ入っていないサーバーで使います。すでにPostgreSQLが動いているサーバーでは使いません。版の切り替えやデータの移行は、別の手順になります。始める前に、サーバー全体のバックアップ(スナップショット)を取っておくと、元に戻せます。

【用途】PostgreSQL 16のサーバーを入れて起動する /【実行場所】サーバー(sudoの使える管理者) /【状態変更】あり(パッケージを入れ、データの領域を作り、サービスを起動する)

sudo dnf module install postgresql:16/server
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql

RHEL 8の公式ドキュメントでは、ストリームを指定してサーバーを入れ、postgresql-setup --initdbでデータの領域を作ってから、systemctlで起動と自動起動を設定しています。

【用途】起動の確認 /【実行場所】AlmaLinuxのサーバー /【状態変更】なし(読み取り)

systemctl status postgresql

【用途】版の確認 /【実行場所】psql(postgresとして接続) /【状態変更】なし(読み取り)

SELECT version();

postgresとしてpsqlに入る方法は、次の節で説明します。

active (running)と、PostgreSQL 16の版が表示されれば準備はできています。うまく動かないまま戻す場合は、取っておいたバックアップから元に戻します。

アプリ専用のユーザーとDBを作る

PostgreSQLでは、ユーザーは「ロール」と呼ばれます。アプリが使うロールを作り、そのロールを所有者にしてDBを作ります。管理用のpostgresのまま、アプリから接続しないようにします。

まだデータの無い新しいDBなのでバックアップは要りませんが、すでに使っているサーバーで行うときは、先にpg_dumpで控えを取っておきます。

【用途】postgresとしてpsqlに入る /【実行場所】サーバー(sudoの使える管理者) /【状態変更】なし(管理用のユーザーに切り替えるだけ)

sudo su - postgres
psql

【用途】ロールとDBを作る /【実行場所】psql(postgres) /【状態変更】あり(ロールとDBを作る)

CREATE ROLE appuser LOGIN PASSWORD '<パスワード>';
CREATE DATABASE appdb OWNER appuser;

<パスワード>には、推測されにくい長い値を使います。PostgreSQL 16では、パスワードの保存方式の既定がscram-sha-256でした。検証で作ったロールも、SCRAM-SHA-256の形で保存されていました。

【用途】ロールの権限の確認 /【実行場所】psql(postgres) /【状態変更】なし(読み取り)

SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolcanlogin
FROM pg_roles WHERE rolname = 'appuser';

検証ではrolsuper・rolcreaterole・rolcreatedbがすべてf(付いていない)、rolcanloginだけがtでした。作り直したいときは、DBにデータを入れる前の段階であれば、管理者が確認したうえでロールとDBを削除して元に戻し、最初から作ります。

権限を必要最小限にする

RHEL 8の公式ドキュメントの例では、ユーザーにCREATEROLEとCREATEDBを付けています。アプリがデータを読み書きするだけなら、どちらも要りません。検証では、これらを付けないロールでも、所有者として指定したDBに表を作ってデータを入れられました。逆に、そのロールでロールやDBを作ろうとすると拒否されました。

ERROR:  permission denied to create role
ERROR:  permission denied to create database

PostgreSQL 15からは、public スキーマに表を作る権限が、すべてのロール(PUBLIC)には付かなくなりました。検証でも、アプリ用のロールは管理用のpostgresのDBには表を作れず、permission denied for schema publicになりました。自分のDBでは、public スキーマの所有者がpg_database_owner(DBの所有者)になっていて、表を作れました。

ただし、接続する権限(CONNECT)は、既定ですべてのロールに付いています。検証でも、アプリ用のロールでpostgresのDBに接続できました。他のDBへの接続も止めたいときは、次のように取り消します。取り消すとpermission denied for database "postgres"で接続が拒否されました。

【用途】他のDBへの接続を止める(任意) /【実行場所】psql(postgres) /【状態変更】あり(権限を取り消す)

REVOKE CONNECT ON DATABASE postgres FROM PUBLIC;

元に戻すときはGRANT CONNECT ON DATABASE postgres TO PUBLIC;を実行します。

127.0.0.1からだけ接続できるようにする

PostgreSQLは、既定ではTCP/IPの接続をlocalhostだけで待ち受けます。アプリが同じサーバーにあるなら、この既定のまま、外から直接は届かないようにします。

誰がどのDBに接続できるかは、pg_hba.confで決めます。hostの行はTCP/IPの接続に当てはまり、上から順に、最初に一致した行が使われます。まず、ファイルの場所を確かめ、変更の前に控えを取ります。

【用途】pg_hba.confの場所の確認 /【実行場所】psql(postgres) /【状態変更】なし(読み取り)

SHOW hba_file;

【用途】変更の前の控え /【実行場所】AlmaLinuxのサーバー /【状態変更】あり(控えのファイルを作る)

sudo cp -p <表示されたパス> <表示されたパス>.bak

次に、アプリ用の行を、他のhostの行より上に足します。

【用途】アプリ用の行を足す /【実行場所】サーバー(エディターでpg_hba.confを開く) /【状態変更】あり(設定ファイルを変更する)

host    appdb    appuser    127.0.0.1/32    scram-sha-256

変更を読み直させて、接続できるかを確かめます。

【用途】設定の再読み込みと確認 /【実行場所】AlmaLinuxのサーバー /【状態変更】あり(設定を読み直す)

sudo systemctl reload postgresql
psql -h 127.0.0.1 -U appuser -d appdb -c "SELECT current_user;"

パスワードを聞かれ、appuserと表示されれば成功です。うまくいかないときは、控えの.bakをもとのファイルに戻し、もう一度systemctl reload postgresqlで読み直せば元に戻ります。

検証では、行の順番によって結果が変わりました。

pg_hba.confの状態 結果
接続先に合う行が無い(appuserがpostgresのDBへ接続) no pg_hba.conf entry for host "127.0.0.1", user "appuser", database "postgres" で拒否
rejectの行がアプリ用の行より上にある pg_hba.conf rejects connection for host ... で拒否
アプリ用の行が上にある 接続できる

アプリの.envに接続文字列を置く

アプリからは、接続文字列で接続します。SQLAlchemyとpsycopg2を使う場合は、次の形です。

【用途】接続文字列 /【実行場所】アプリのディレクトリの.env /【状態変更】あり(ファイルを作る)

DATABASE_URL=postgresql+psycopg2://appuser:<パスワード>@127.0.0.1:5432/appdb

.envはGitに入れず(.gitignoreに書く)、ファイルの権限を所有者だけが読める状態にします。パスワードに記号を含む場合は、URLとして使える形に直す必要があります。

【用途】接続の確認 /【実行場所】アプリの仮想環境 /【状態変更】なし(読み取り)

import os

from sqlalchemy import create_engine, text

engine = create_engine(os.environ["DATABASE_URL"])
with engine.connect() as conn:
    print(conn.execute(text("SELECT current_user, current_database()")).one())

検証では、SQLAlchemy 2.0.35とpsycopg2 2.9.9から、scram-sha-256のロールとして接続できました。このサイトのアプリも、接続文字列は.envに置いています。アプリの起動のしかたはFastAPIアプリをGunicornとUvicornで公開する手順を参考にしてください。

変更の前にpg_dumpを取り、前後で件数を比べる

表の変更やデータの入れ替えの前には、そのDBの控え(ダンプ)を取ります。カスタム形式(-Fc)で取ると、中身の一覧を確かめられます。

【用途】変更の前のバックアップ /【実行場所】AlmaLinuxのサーバー /【状態変更】あり(ダンプのファイルを作る)

pg_dump -h 127.0.0.1 -U appuser -Fc -f appdb_before.dump appdb
pg_restore --list appdb_before.dump | grep "TABLE DATA"

pg_restore --listは中身の一覧を表示するだけで、DBは変えません。検証では、一覧にTABLE DATA public itemsが出て、表のデータが入っていることを確かめられました。

あわせて、変更の前と後で件数を比べます。

【用途】前後の件数の確認 /【実行場所】psql(appuser) /【状態変更】なし(読み取り)

SELECT count(*) FROM items;

検証では、変更の前に2件、1件を追加した後に3件と、想定どおりに変わったことを確かめました。想定と違う件数になったら、そこで止めます。元に戻すときは、今の状態もpg_dumpで控えたうえで、取っておいたダンプから戻します。戻す手順と、戻せることの確かめ方はサーバーのバックアップと復元確認で紹介しています。

つまずきやすい点

検証で表示された誤りと、その意味です。

  • password authentication failed for user "appuser":パスワードが違う。または、パスワードがmd5で保存されたまま、pg_hba.confの方式がscram-sha-256になっている
  • no pg_hba.conf entry for host ...:送り元・ユーザー・DBの組み合わせに合う行が無い
  • pg_hba.conf rejects connection for host ...:rejectの行に先に一致している
  • permission denied for schema public:自分が所有していないDBのpublic スキーマに表を作ろうとしている
  • permission denied for database ...:そのDBへのCONNECTが取り消されている

md5については、検証で、md5で保存したパスワードのロールが、scram-sha-256の行では接続できず、md5の行では接続できました。古い版から移ったDBでは、password_encryptionがscram-sha-256の状態で、パスワードを設定し直してください。

このサイトで実際にしていること

このサイトのアプリ用のロールも、自分のDBの所有者だけで、スーパーユーザー・ロールの作成・DBの作成の権限は持たせていません。表はすべてアプリ用のロールが所有し、接続はlocalhostだけで受けています。DBを変更する作業の前には、必ずバックアップを取り、変更の前と後で件数を比べてから進めています。

Claude Codeに任せる範囲の判断

ロールの作成、パスワードの設定、pg_hba.confの変更は、間違えると接続できなくなったり、守りが弱くなったりします。Claude Codeには、まず読み取りだけを頼み、結果を見てから自分で実行します。読み取りの考え方はサーバーの状態確認をClaude Codeに読み取り専用で任せる方法にまとめています。

【用途】DBの作業の前に、現状の読み取りだけを頼む /【実行場所】Claude Code /【状態変更】なし(変更の前に必ず止めて報告させる)

PostgreSQLにアプリ用のロールとDBを作る準備として、サーバーの状態を読み取りだけで確認してください。
- dnf module list postgresql で、有効になっているストリームと版
- PostgreSQLが動いているか(systemctl status postgresql)
- pg_hba.conf の場所と、host の行の一覧(内容は表示するだけ)
ロールの作成・パスワードの設定・設定ファイルの変更・サービスの再起動はしないでください。
結果と、次に実行するSQLとコマンドの案を報告して、私の確認を待ってください。

パスワードは、Claude Codeへの依頼文やチャットに書かず、自分で入力します。サーバー全体の作業の流れはClaude CodeでAlmaLinuxサーバーを構築・運用する方法を参考にしてください。

まとめ

  • AlmaLinux 8の既定のストリームは支援の終わった10なので、16のように支援中の版を指定して入れる
  • アプリ用のロールはLOGINだけで作り、DBの所有者にする。CREATEDB・CREATEROLE・スーパーユーザーは付けない
  • pg_hba.confでは、アプリ用の行を127.0.0.1とscram-sha-256で、他の行より上に書く。変更の前に控えを取る
  • 接続文字列は.envに置き、Gitに入れない
  • 変更の前にはpg_dumpを取り、前後で件数を比べる

まずはdnf module list postgresqlで、入る版を確かめるところから始めてみてください。