Article
🐳 Docker Composeのdepends_onだけではDBを待てない:healthcheckとservice_healthyの使い方
Docker ComposeでAPIとPostgreSQLを同時に起動したとき、DBコンテナは起動しているのに接続エラーになることがあります。
原因の一つは、コンテナの起動(running)と、DBが接続を受け付けられる状態(ready)を混同していることです。この記事では、2026年10月13日時点のDocker公式ドキュメントをもとに、depends_on・healthcheck・service_healthyを実務でどう使い分けるか整理します。
なぜdepends_onだけでは不十分なのか
以下の短い書き方は、apiより先にdbを起動する順序を指定します。
services:
api:
build: ./api
depends_on:
- db
db:
image: postgres:18
しかし、PostgreSQLは起動後に内部の初期化を行うため、コンテナが動き始めてもすぐにSQLを実行できるとは限りません。Docker Composeの公式説明でも、短いdepends_onは依存先が「healthy」になるまで待つことを保証しないとされています。
ここで用語を区別します。
- started / running:コンテナのプロセスが起動した状態。
- healthy:コンテナに設定したヘルスチェックが成功している状態。
- readiness(準備完了):アプリから見て、必要な処理を受け付けられる状態。何を確認すべきかはアプリによって異なります。
つまり、depends_on: [db]を付けるだけでDB接続エラーが必ず解消するわけではありません。
動作を確認できるCompose設定
次のcompose.yamlは、DBのヘルスチェックを待ってから、別コンテナでSELECT 1を実行する最小例です。ホストPCにPostgreSQLをインストールする必要はありません。
services:
db:
image: postgres:18
environment:
POSTGRES_USER: app
POSTGRES_DB: appdb
POSTGRES_PASSWORD: ${DB_PASSWORD:?Set_DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
interval: 5s
timeout: 3s
retries: 10
start_period: 15s
check:
image: postgres:18
depends_on:
db:
condition: service_healthy
environment:
PGPASSWORD: ${DB_PASSWORD:?Set_DB_PASSWORD}
command:
- sh
- -ec
- "psql -h db -U app -d appdb -tAc 'SELECT 1'"
.envファイルには、ローカル検証用のパスワードを設定します。
DB_PASSWORD=replace-with-a-local-test-password
実運用のパスワードを記事・リポジトリへコミットしないでください。.envもGitの追跡対象に含めず、本番では利用環境に応じたシークレット管理を検討してください。また、この例は学習用のためDBの永続化ボリュームを設定していません。
ヘルスチェックの意味
pg_isready:PostgreSQLサーバーが接続要求を受け付ける状態かを調べる付属コマンドです。スキーマ作成済み・必要な権限が正しいことまでは保証しません。CMD-SHELL:コンテナ内のシェルでチェック用コマンドを実行します。interval: 5s:チェックの実行間隔を指定します。timeout: 3s:チェック1回のタイムアウトです。retries: 10:一定回数の連続失敗でunhealthyと判定します。start_period: 15s:起動直後の準備期間です。この期間中の失敗の扱いは通常の連続失敗とは異なります。
$${POSTGRES_USER}の二重ドル記号も重要です。Composeは$から始まる値をホスト側で変数展開しようとします。$$と書くと展開を避け、コンテナ内のシェルがPOSTGRES_USERを参照できるようにします。
checkサービスのservice_healthyは「dbのヘルスチェックが成功してから起動する」条件です。checkはDBへ接続してSELECT 1が成功すると正常終了します。長時間稼働するAPIを模したものではなく、起動待ちを試すための一回限りの確認役です。
検証コマンドと、それぞれの意味
compose.yamlと.envを同じディレクトリに置き、Docker EngineとComposeが使える環境で実行します。
docker compose config -q
docker compose up -d
docker compose ps -a
docker compose logs --follow db
docker compose config -q:Composeの設定を解析して検証します。-qは正常時の設定内容の表示を抑えるオプションです。実際にDBへ接続できるかは検査しません。docker compose up -d:構成に従ってコンテナを作成・起動します。-dはバックグラウンド実行(detached)です。docker compose ps -a:終了したコンテナも含めて状態を表示します。dbがhealthyとなり、checkが終了コード0になれば例の確認は成功です。docker compose logs --follow db:dbのログを継続表示します。--followは追加ログを追いかける指定で、終了はCtrl+Cです。
停止・片付けには次を使います。
docker compose down
downはComposeで作成したコンテナなどを停止・削除します。この例はDBデータを永続化していないので、downでコンテナを消すとテストデータも失われます。永続データのある実環境ではボリューム設計を別途行ってください。
本番向けAPIに置き換えるなら
既存のAPIサービスがあれば、起動条件を次のように定義できます。
services:
api:
build: ./api
depends_on:
db:
condition: service_healthy
environment:
DB_HOST: db
DB_PORT: "5432"
実際には、これを先ほどのdb定義と組み合わせ、DBユーザー名や認証情報なども安全に渡してください。Composeの同じネットワークに属するコンテナ同士は、通常サービス名dbで通信できます。APIコンテナ内のlocalhostはAPIコンテナ自身なので、PostgreSQLへつなぐホスト名としては通常使いません。
ただし、service_healthyはアプリの再接続処理の代わりではありません。DBが起動後に再起動したり、一時的にネットワークが切れたりする可能性があるため、API側にも適切な接続タイムアウト、リトライ、接続プールの回復処理が必要です。
「マイグレーション完了」を待ちたい場合
DBが接続可能でも、テーブルが作成済みとは限りません。マイグレーション(テーブル構造などを変更する処理)をワンショットのサービスに分ける場合は、service_completed_successfullyという別の条件が使えます。
services:
migrate:
build: ./api
command: ["npm", "run", "db:migrate"]
depends_on:
db:
condition: service_healthy
api:
build: ./api
depends_on:
migrate:
condition: service_completed_successfully
これは既存のdb定義に追加する構成例であり、./apiにマイグレーション用スクリプトが存在することが前提です。マイグレーションが正常終了した後にAPIを開始できます。ただし、マイグレーション自体は同時実行・失敗後の再実行・後方互換性も考えて設計してください。
よくあるハマりどころ
1. healthyならアプリから必ず使えると考える
DBプロセスの状態と、テーブル・ユーザー権限・外部依存の準備状況は別物です。業務上の準備完了条件があるなら、それを確認するチェックを用意します。
2. ヘルスチェック用コマンドがコンテナ内にない
curlなどでチェックするとき、利用するイメージにその実行ファイルがなければ失敗します。今回の例ではPostgreSQLイメージに付属するpg_isreadyを使います。
3. restart: trueで障害後に自動でAPIも再起動すると期待する
depends_onの長い書き方で指定するrestart: trueは、Docker Composeで明示的に依存サービスを再起動・更新する操作と連動するための設定です。DBコンテナが自動再起動した際にAPIも必ず再起動される、という意味ではありません。サービス直下の再起動ポリシーとも区別してください。
4. パスワードをCompose設定に直接書いてしまう
ローカル検証の.envと、運用環境の秘密情報管理は分けましょう。docker compose configは変数の展開結果を表示するため、共有ログへ出力する際も注意が必要です。
まとめ:依存関係とアプリの耐障害性は別に考える
- 起動順だけでよい:短い
depends_on(またはservice_started)。 - DBのヘルスチェック成功まで待つ:
healthcheck+service_healthy。 - 一回限りの初期化処理が正常終了してから起動する:
service_completed_successfully。 - 起動後にDBが落ちても復旧したい:API自身の接続回復・リトライを設計する。
Composeの起動順制御は便利ですが、データベースや他サービスとの通信が常に成功する保証ではありません。**「起動時に待つ仕組み」と「稼働中の障害から回復する仕組み」**を分けて設計すると、ローカル開発やCIの不安定さを減らしやすくなります。