できない.dev

docker compose の depends_on で起動順が待たれない

depends_on の短い書き方はコンテナが起動するまでしか待たず、DB などが受付可能になるのは待たない。condition: service_healthy と healthcheck を組み合わせて準備完了まで待たせる。

公開: 更新:

実行例あり(2026-09-23 に実環境で検証)

要約

depends_on を書いてもアプリが DB より先に動いてしまうのは、短縮記法の depends_on が「依存先コンテナの起動」までしか待たないためです。
公式ドキュメントも「Compose はコンテナが ready になるまでは待たず、起動しているところまでしか待たない」と明記しています。
データベースのように起動後に初期化時間が必要なサービスでは、healthcheck で準備完了を判定し、depends_on の condition: service_healthy で「健全になるまで待つ」よう指定します。

実行例

短い書き方の depends_on では db コンテナが Started になった直後に app が起動し、pg_isready は db:5432 - no response(終了コード 2)を返した。healthcheck と condition: service_healthy を加えた 2 回目は db が Healthy になるのを待ってから app が起動し、accepting connections が返っている。

$ cat compose.yml
services:
  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_PASSWORD: secret
  app:
    image: postgres:17-alpine
    depends_on:
      - db
    command: ["sh", "-c", "pg_isready -h db -U postgres; echo \"exit=$?\""]
$ docker compose up --abort-on-container-exit app
Network dependsdemo1037_default Creating 
 Network dependsdemo1037_default Created 
 Container dependsdemo1037-db-1 Creating 
 Container dependsdemo1037-db-1 Created 
 Container dependsdemo1037-app-1 Creating 
 Container dependsdemo1037-app-1 Created 
Attaching to app-1
 Container dependsdemo1037-db-1 Starting 
 Container dependsdemo1037-db-1 Started 
 Container dependsdemo1037-app-1 Starting 
 Container dependsdemo1037-app-1 Started 
app-1  | db:5432 - no response
app-1  | exit=2
app-1 exited with code 0
 Compose Stopping Aborting on container exit...
 Container dependsdemo1037-app-1 Stopping 
 Container dependsdemo1037-app-1 Stopped
$ cat compose.yml
services:
  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 2s
      timeout: 3s
      retries: 10
      start_period: 5s
  app:
    image: postgres:17-alpine
    depends_on:
      db:
        condition: service_healthy
    command: ["sh", "-c", "pg_isready -h db -U postgres; echo \"exit=$?\""]
$ docker compose up --abort-on-container-exit app
Network dependsdemo1037_default Creating 
 Network dependsdemo1037_default Created 
 Container dependsdemo1037-db-1 Creating 
 Container dependsdemo1037-db-1 Created 
 Container dependsdemo1037-app-1 Creating 
 Container dependsdemo1037-app-1 Created 
Attaching to app-1
 Container dependsdemo1037-db-1 Starting 
 Container dependsdemo1037-db-1 Started 
 Container dependsdemo1037-db-1 Waiting 
 Container dependsdemo1037-db-1 Healthy 
 Container dependsdemo1037-app-1 Starting 
 Container dependsdemo1037-app-1 Started 
app-1  | db:5432 - accepting connections
app-1  | exit=0
app-1 exited with code 0
 Compose Stopping Aborting on container exit...
 Container dependsdemo1037-app-1 Stopping 
 Container dependsdemo1037-app-1 Stopped

— 2026-09-23 時点の出力

検証環境

検証日
実行環境
local host (Windows 11 Home, Docker 29.1.3)
バージョン
  • Node.js 22.14.0
  • npm 10.9.2
  • Git 2.48.1.windows.1
  • Docker 29.1.3

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. 短縮記法の限界: depends_on: [db] はコンテナが立ち上がった時点で次へ進み、DB が接続を受け付けるかは見ない。
  2. 初期化の時間差: DB は起動してから接続受付までに数秒かかり、その間にアプリが接続して失敗する。
  3. healthcheck 未定義: 準備完了の判定基準が無いため、待ちようがない。
  4. リトライが無い: アプリが起動時の一度の接続失敗でクラッシュしている。

解決策

1. 依存先に healthcheck を定義する

依存される側(例: PostgreSQL)に、受付可能かを判定するヘルスチェックを定義します。

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

2. depends_on を長い記法にして条件を付ける

depends_on を配列ではなくマップで書き、service_healthy を待ち条件に指定します。
これで db のヘルスチェックが通るまで app は起動しません。

  app:
    build: .
    depends_on:
      db:
        condition: service_healthy

condition には service_started(起動のみ)、service_healthy(健全になるまで)、service_completed_successfully(正常終了まで)が使えます。

3. アプリ側のリトライも併用する

ヘルスチェックを入れても、ネットワークの揺らぎや再起動で一時的に接続が切れることはあります。
最終的な堅牢性のため、アプリ側にも接続リトライ(指数バックオフ)を入れておくと安定します。

この記事は役立ちましたか?