PostgreSQL 18とpgvectorのrootless Quadlet構成
PostgreSQL 18、LLVM JIT、pgvectorをPodmanとQuadletで動かす構成です。業務システム開発やLLM導入のデータ基盤に向け、設定、永続化、起動手順を整理しました。
構成の概要
PostgreSQL 18 を rootless Podman + Quadlet で動かす構成を整理しました。LLVM JIT と pgvector を有効にし、設定と実データの置き場を分けています。
起動は systemd --user で管理します。イメージ、外部設定、永続データを別々に更新できる構成です。
用途と要件
単ノードのデータ基盤に、次の2つを用意します。
- LLVM JIT によるクエリ最適化
- pgvector 拡張でのベクトル検索対応
Podman + Quadlet の rootless 構成を使い、設定とデータを NVMe の /mnt/data に保存します。業務システム開発のDBや、LLM導入で使うベクトル検索の基盤を想定しています。
ディレクトリ構成
イメージと構成ファイルを次の場所に置きます。
/opt/containers/runtime/postgresql/
├── etc/
│ ├── postgresql.conf
│ ├── pg_hba.conf
│ └── docker-entrypoint-initdb.d/
│ └── 010-create-vector.sql
├── Dockerfile
└── postgresql.container
実データは NVMe に分けて保存します。
/mnt/data/postgresql/data/
Dockerfile と postgresql.container は構成定義です。postgresql.conf と pg_hba.conf はコンテナ外に置き、DB 本体は /mnt/data/postgresql/data/ に保存します。イメージの再構築とデータ管理を分けるためです。
Dockerfile(pg18 + LLVM + pgvector)
postgres:18-trixie をベースに、pgvector のビルド用パッケージを追加しました。
FROM postgres:18-trixie
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
build-essential clang llvm-dev git ca-certificates pkg-config make postgresql-server-dev-18; \
rm -rf /var/lib/apt/lists/*
ARG PGVECTOR_VERSION=v0.8.1
RUN git clone --depth 1 --branch "$PGVECTOR_VERSION" https://github.com/pgvector/pgvector.git /tmp/pgvector && \
make -C /tmp/pgvector && make -C /tmp/pgvector install && rm -rf /tmp/pgvector
RUN sed -i 's/#jit = off/jit = on/' /usr/share/postgresql/postgresql.conf.sample \
&& printf '\njit_above_cost = 10000\n' >> /usr/share/postgresql/postgresql.conf.sample
COPY 010-create-vector.sql /docker-entrypoint-initdb.d/010-create-vector.sql
pgvector は v0.8.1 を浅く clone し、バージョンを固定しています。postgresql-server-dev-18 で PostgreSQL 18 用のヘッダを用意します。
サンプル設定の #jit = off を変更し、jit_above_cost = 10000 を追加しました。イメージ内と外部 postgresql.conf の両方で JIT を有効にしています。
初期化 SQL で vector 拡張を作成します。
CREATE EXTENSION IF NOT EXISTS vector;
DB 初期化直後から vector 拡張を利用でき、アプリのマイグレーションや接続テストで拡張の有無を確認できます。
postgresql.conf(メモリ 4GB 想定)
メモリ 4GB を想定した設定です。
listen_addresses = '*'
port = 5432
max_connections = 200
shared_buffers = 1GB
effective_cache_size = 3GB
maintenance_work_mem = 256MB
work_mem = 32MB
random_page_cost = 1.1
effective_io_concurrency = 200
max_worker_processes = 8
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
# WAL
wal_buffers = 16MB
min_wal_size = 512MB
max_wal_size = 2GB
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9
log_checkpoints = on
# ログ
log_destination = 'stderr'
logging_collector = on
log_min_duration_statement = 200ms
log_line_prefix = '%m [%p] %q%u@%d '
# JIT
jit = on
jit_above_cost = 10000
jit_optimize_above_cost = 50000
jit_inline_above_cost = 100000
# ロケール
lc_messages = 'C'
lc_monetary = 'C'
lc_numeric = 'C'
lc_time = 'C'
shared_buffers = 1GB、effective_cache_size = 3GB、work_mem = 32MB、maintenance_work_mem = 256MB に設定しました。
NVMe を想定し、random_page_cost = 1.1、effective_io_concurrency = 200 としました。checkpoint_completion_target = 0.9 で WAL のチェックポイント書き込みを平準化します。
stderr と logging_collector = on を使い、200ms 以上のクエリを記録します。jit = on に加え、最適化と inline のコスト閾値も指定しています。
pg_hba.conf
認証設定です。
# TYPE DATABASE USER ADDRESS METHOD
local all all trust
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
host all all 192.168.0.0/16 scram-sha-256
host all all 10.10.0.0/16 scram-sha-256
UNIX ソケットは trust、TCP は scram-sha-256 を使います。許可範囲は 192.168.0.0/16 と 10.10.0.0/16 です。接続元の範囲は運用環境に合わせて確認する必要があります。
環境変数ファイル
環境変数は ~/.config/containers/systemd/.postgresql.env に置きます。
POSTGRES_USER=postgres
POSTGRES_PASSWORD=localdev
POSTGRES_DB=appdb
ファイルの権限を制限します。
chmod 600 ~/.config/containers/systemd/.postgresql.env
Quadlet の EnvironmentFile は [Container] に指定します。[Service] に置くと期待どおりに反映されません。
Quadlet: postgresql.container
Quadlet の定義です。
[Unit]
Description=PostgreSQL 18 (LLVM/JIT + PG Vector)
Wants=network-online.target
After=network-online.target
[Container]
Image=compute.home.arpa/pg18-jit-vec:latest
ContainerName=postgresql
Network=slirp4netns:allow_host_loopback=true
PublishPort=5432:5432
Tmpfs=/dev/shm:size=4g
Volume=/mnt/data/postgresql/data:/var/lib/postgresql/data:rw
Volume=/opt/containers/runtime/postgresql/etc/postgresql.conf:/etc/postgresql/postgresql.conf:ro
Volume=/opt/containers/runtime/postgresql/etc/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
EnvironmentFile=%h/.config/containers/systemd/.postgresql.env
Exec=postgres -c config_file=/etc/postgresql/postgresql.conf -c hba_file=/etc/postgresql/pg_hba.conf
Ulimit=nofile=1048576:1048576
[Service]
Restart=always
[Install]
WantedBy=default.target
PublishPort=5432:5432 でホスト側に公開し、slirp4netns:allow_host_loopback=true を使います。ホストから 127.0.0.1 への接続を確認する構成です。
Tmpfs=/dev/shm:size=4g で共有メモリを確保し、Ulimit=nofile=1048576:1048576 でファイルディスクリプタ上限を指定しています。
設定は /etc/postgresql/ に RO マウントし、Exec=postgres -c config_file=… -c hba_file=… で指定します。起動時に使うファイルをユニット定義で確認できます。
起動手順
起動前にデータディレクトリを作り、所有者を 999:999 に合わせます。
mkdir -p /mnt/data/postgresql/data
sudo chown -R 999:999 /mnt/data/postgresql/data
systemctl --user daemon-reload
systemctl --user enable --now postgresql.container
systemctl --user status postgresql
永続ディレクトリの所有者をコンテナ内ユーザーに合わせます。rootless でも、権限が合わなければ初期化や書き込みが失敗します。
記録した手順では、生成された .service ではなく .container を enable しています。
動作確認
JIT と vector 拡張を確認します。
psql -h 127.0.0.1 -U postgres -d appdb -c "SHOW jit;"
psql -h 127.0.0.1 -U postgres -d appdb -c "SELECT * FROM pg_extension;"
期待する出力です。
jit
-----
on
name | version | schema | description
--------+---------+------------+----------------------------------------
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
vector | 0.8.1 | public | vector data type and ivfflat and hnsw access methods
この2つのチェックは、JIT の有効化と vector 拡張の導入を確認するものです。
チューニング・トラブルシュート
起動や設定で確認する点を表にまとめました。
| 事象 | 原因 | 対処 |
|---|---|---|
| chown: Read-only file system | conf を /var/lib/postgresql/… に RO マウントしている | /etc/postgresql/ へ移動または :rw 指定 |
| .env が反映されない | [Service] に置いている | [Container] EnvironmentFile= に移動 |
| Unit is transient or generated | .service は生成物 | .container を enable |
| /etc/containers/systemd/users/1000 が無い | 通常 | 無視可 |
| initdb 変数が効かない | 既に DB 初期化済み | data 削除して再起動 or SQL で変更 |
設定をデータディレクトリ側に RO マウントすると、chown: Read-only file system が起こる場合があります。/etc/postgresql/ に置いて、この衝突を避けています。
環境変数が反映されない場合は、[Container] EnvironmentFile= に指定しているかを確認します。
initdb 用の変数は、初期化済みのデータディレクトリには適用されません。既存データがあるかを確認する必要があります。
結果
PostgreSQL 18 + LLVM JIT + pgvector を rootless Quadlet で動かすための、ビルド、設定、永続化、起動、確認手順を揃えました。
今後の作業
初期化は COPY だけで行うか、コメントアウトした Volume マウントも使うかを、今後統一する予定です。
共有環境に広げる場合は、192.168.0.0/16 と 10.10.0.0/16 の許可範囲を見直す予定です。
POSTGRES_PASSWORD=localdev はローカル開発用です。共有サーバーや長期運用では、秘密情報の管理を別に用意する必要があります。
