Docker公式イメージでPostgreSQLを立ち上げたのにコンテナが即Exitする、ホストのpsqlから接続できない、docker compose downを実行したらデータが消えた。この手の症状は原因のパターンが限られています。前回の起動で作られたボリュームが残っているか、接続先のホスト名とポートを取り違えているか、だいたいこの2つに収まります。
この記事は要約版です。エラーメッセージ別の対処手順、
shm_sizeやチェックポイント設定の調整、ヘルスチェックまで含めた壊れにくいcompose構成例は、ブログの完全版にまとめています: PostgreSQL Docker起動エラー切り分け手順
結論は「残ったボリューム」と「接続先の指定」
公式postgresイメージのエントリポイントは、PGDATAが空のときだけinitdbを実行します。つまりPOSTGRES_PASSWORD、POSTGRES_USER、POSTGRES_DB、/docker-entrypoint-initdb.d/配下のスクリプトが有効になるのは初回起動時だけです。ボリュームが残った状態でdocker-compose.ymlのパスワードやDB名を書き換えても、既存クラスタの中身は変わりません。その結果、password authentication failed for user "app"やdatabase "app" does not existが出たり、初期化SQLが実行されないままになります。
接続系は、どこから繋ごうとしているかで指定が変わります。ホストOSのpsqlやGUIツールからはlocalhostとports:の左側の番号、同じCompose内の別コンテナからはサービス名とコンテナ側の5432、コンテナ内のpsqlはホスト指定なしでUNIXドメインソケットを使います。アプリコンテナからlocalhost:5432を指定するとアプリ自身のコンテナを見にいくため、Connection refusedになります。
切り分けの順番はログ、ボリューム、ネットワークです。推測で設定を書き換える前にdocker compose logs --tail=50 dbで終了理由を読み、database system is ready to accept connectionsが出ているならPostgreSQL本体は起動済みと判断してネットワーク側へ進みます。
先に確認したいボリュームの状態
データディレクトリの実体は、使い捨てコンテナでマウントすると手早く確認できます。
# データディレクトリが初期化済みか、どのメジャーバージョンで作られたか
docker run --rm -v pgdata:/v alpine sh -c 'ls -A /v | head; cat /v/PG_VERSION 2>/dev/null'
ls -Aが空なら未初期化なので、環境変数と初期化SQLが効きます。PG_VERSIONの値とイメージタグのメジャーバージョンが食い違っていれば、データファイルの非互換で起動しません。
完全版で扱っている内容
- 原因の切り分け手順として、
ExitCode: 1と137/OOMKilledの読み分け、初期化フェーズと起動フェーズのログの区別、コンテナ内ソケット→コンテナ内TCP→別コンテナ→ホスト公開ポートの4段階テストと、どこで失敗したかに応じた原因の特定 - エラーメッセージ別の対処として、
Database is uninitialized and superuser password is not specified、directory ... exists but is not empty、has invalid permissions、database files are incompatible with server、incorrect checksum in control fileへの具体的な対応 - 起動はするが遅い・不安定なケースとして、
/dev/shmが既定64MBであることと動的共有メモリのエラー、shm_sizeの指定、Docker Desktopでのバインドマウントと名前付きボリュームの使い分け、max_wal_sizeやlog_min_duration_statementをcommandで渡す方法 - よくある質問として、初期化SQLが実行されない4つの条件、
pg_dumpallとpg_upgradeによるメジャーバージョン移行の手順、本番でコンテナ運用する場合に自分側へ残る責務、停止時と稼働時それぞれのバックアップ手順 - 再発防止の構成例として、パッチバージョンまでのタグ固定、
127.0.0.1:付きのポート公開、healthcheckとdepends_onのcondition: service_healthyの組み合わせ