構成の概要

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 systemconf を /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 はローカル開発用です。共有サーバーや長期運用では、秘密情報の管理を別に用意する必要があります。