メインコンテンツまでスキップ

第09章:初期化の王道:docker-entrypoint-initdb.d でDBに種を入れる🌱

この章は「DBコンテナを初回起動した瞬間に、スキーマ作成+初期データ投入まで終わってる状態」を作ります✨ (やることはシンプル:init/ にSQLやシェルを置くだけ!)


1) まず“仕組み”を1分で理解しよう🧠💡

Postgres公式イメージは、初回起動でデータディレクトリが空のときだけ👇をやります。

  1. initdb(DBの初期作成)

  2. /docker-entrypoint-initdb.d 配下のファイルを実行

    • *.sql を実行
    • *.sql.gz も実行
    • *.sh実行可能なら実行/実行不可なら source(読み込み実行)
  3. その後、Postgres本体を起動🚀

しかも、**実行順はファイル名のソート順(ロケール基準)**なので、00_ とか 10_ とか付けると事故りにくいです。(Docker Hub)

超重要:2回目以降は「データが既にある」扱いになり、初期化スクリプトは基本動きません。(Docker Hub) さらに、途中で初期化スクリプトが失敗して再起動されると「半端に初期化されたデータが残る→以後スクリプト続きが走らない」事故も起きがちです。(Docker Hub)


2) 2026年の注意点:Postgres 18から“データのマウント先”が変わった⚠️📦

2026/02/11時点の最新メジャーは PostgreSQL 18(2025/09/25リリース)です。(PostgreSQL) そして **Postgres 18以降、デフォルトのPGDATAが「バージョン別のパス」**になりました。さらに DockerfileのVOLUMEも変更されています。(Docker Hub)

  • 18のPGDATA例:/var/lib/postgresql/18/docker
  • 18+ のVOLUME/var/lib/postgresql
  • なので、永続化用のvolumeは /var/lib/postgresql にマウントするのが安全💪(Docker Hub)

ここ、古い記事のまま /var/lib/postgresql/data にマウントして「なんか永続化されない…」ってなりがちなので、最初から最新の流儀でいきましょ😇


3) ハンズオン:最小構成で「初回起動で全部入る」を作る🧩✨

✅ フォルダ構成(これが“王道テンプレ”)

myapp/
compose.yml
init/
00_schema.sql
10_seed.sql
90_note.sql
README.md

compose.yml(Postgres 18 + named volume + init)

services:
db:
image: postgres:18
ports:
- "5432:5432"
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: apppass
POSTGRES_DB: appdb
volumes:
# ✅ 永続化(Postgres 18+ はここが推奨)
- db-data:/var/lib/postgresql

# ✅ 初期化スクリプト置き場(ホスト→コンテナへ)
- ./init:/docker-entrypoint-initdb.d:ro

volumes:
db-data:

ポイント:Composeのトップレベルvolumes:で named volume を定義して、サービス側から参照します📦(Docker Documentation)

00_schema.sql(テーブル作成)

CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

10_seed.sql(初期データ投入)

INSERT INTO users (name) VALUES
('Alice'),
('Bob'),
('Carol');

✅ 起動!

docker compose up -d

✅ 「初期化が走った証拠」をログで見る🔎

docker compose logs db -f

ログに /docker-entrypoint-initdb.d/... が出てたら勝ち🏆(実行順もファイル名順で出ます)(Docker Hub)

✅ DBを覗いて確認👀

docker compose exec db psql -U app -d appdb -c "SELECT * FROM users;"

4) ちょい上級:*.sh で「ユーザー作る」「複数DB作る」もできる🛠️😎

公式の例だと、*.shpsql を叩いてユーザーやDBを作るのも王道です。(Docker Hub) 例(20_init.sh):

#!/usr/bin/env bash
set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER readonly;
GRANT CONNECT ON DATABASE appdb TO readonly;
EOSQL

*.sh実行可能なら実行、そうでなければ source されます。(Docker Hub) (Windowsだと実行ビットが付かないこともあるので、sourceされても動く書き方に寄せるのがラク👍)


5) ありがちな罠10連発🪤😇(これだけで事故が激減する)

  1. 「2回目に動かない」 → 正常です。初回(データが空のとき)だけです。(Docker Hub)

  2. 初期化スクリプトが途中でコケたのに、再起動で続きが走らない → 公式が“よくある問題”として明記してます。(Docker Hub) 対策:まずログで失敗地点を潰す/必要ならボリュームを捨てて作り直す(次章でやるやつ💣)

  3. Postgres 18なのに古いマウント先で永続化できてない → 18+ はマウント先の考え方が変わりました。/var/lib/postgresql へ。(Docker Hub)

  4. init/ のファイル名の順番が悪くて、seedが先に走って死ぬ → 実行順は“ソート順”。00_ 10_ みたいに番号付けで安定。(Docker Hub)

  5. .sh が Windows の CRLF で死ぬbash\rとか) → VS Codeで改行をLFに、Gitもeol管理(.gitattributes)がおすすめ💡

  6. SQLのどれかが失敗して止まってるのに気づかない.shpsql -v ON_ERROR_STOP=1 を使うと、失敗を即検知できて強い💪(Docker Hub)

  7. パスワード無しで繋がって「あれ?」 → コンテナ内部(Unix socket)だと trust で繋がることがある、という注意が公式にあります。(Docker Hub)

  8. 「マウントしたら中身が消えた?」 → bind mount は、コンテナ内の既存ファイルを“隠す”動きをします。(Docker Documentation) (/docker-entrypoint-initdb.d なら問題になりにくいけど、別パスで事故りがち)

  9. 秘密情報を init SQL に直書きしてしまう → それは危険😱 公式は _FILE で secrets を読むやり方も案内してます。(Docker Hub)

  10. 「初期化=migration」だと思ってしまう → 初期化は“最初の種”。変更履歴は migration で管理(次の章あたりに繋がるやつ)📜✨


6) 演習ミッション🎯(15分でできる)

ミッションA:順番事故をわざと起こす🧨→直す🧯

  1. 10_seed.sql00_seed.sql にリネーム
  2. 起動してログで失敗を確認
  3. 00_schema.sql10_seed.sql に戻して成功させる → 「順番の大切さ」が体に染みます😂

ミッションB:sql.gz を使ってみる🗜️

  1. 10_seed.sql を gzip して 10_seed.sql.gz にする
  2. 初回起動で入るのを確認 → 公式でサポートされてます。(Docker Hub)

7) AIの使いどころ🤖✨(危なくしない使い方)

  • ✅ 「このテーブル定義で、最小のseedデータ案を5件作って」
  • ✅ 「このDDL/seedを、初期化用途として安全にするチェックリスト出して」
  • ✅ 「docker-entrypoint-initdb.d が動かない時の原因候補を3つ、確認コマンド付きで」

⚠️ ただし、実プロダクトの秘密(本物のAPIキー/パスワード/顧客データ)を貼らないのは鉄則ね🔒


8) この章の成果物📦🎉

  • init/ フォルダ(00_schema.sql + 10_seed.sql + README)
  • compose.yml(Postgres 18 / 永続化 / initマウント)
  • 「初回だけ動く」「順番がある」「18+のマウント先が新しい」←この3点が腹落ち🧠✨

次の章(第10章)は、この章の続きで超あるあるな **「2回目に動かない罠」**を“わざと踏んで”、最短で直すリセット手順を作ります🪤➡️🧯 (down -v をどう安全に扱うか、がメイン!)