Article

🐳 Docker Composeのdepends_onだけではDBを待てない:healthcheckとservice_healthyの使い方

docker, dockercompose, postgresql, backend

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の不安定さを減らしやすくなります。

公式参考資料(2026年10月13日確認)