Docker Composeのdepends_onとHEALTHCHECK|サービス起動順序を確実に制御する設計パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Docker > Docker Composeのdepends_onとHEALTHCHECK|サービス起動順序を確実に制御する設計パターン
「docker-compose up を実行したのに、アプリコンテナがDB接続エラーで即座に落ちる」——この問題の原因は、DBコンテナのプロセスが起動しても「接続を受け付ける準備」が整う前にアプリが接続を試みることです。

Docker Compose の depends_oncondition: service_healthy を指定し、Dockerfile の HEALTHCHECK 命令を組み合わせると、DBが完全に起動するまでアプリコンテナの起動を遅らせることができます。

この記事では、depends_on の3種類のconditionとHEALTHCHECKの設定方法を、PostgreSQL・MySQL・Redisへの適用例を交えて解説します。curl・nc・独自スクリプトを使ったヘルスチェックパターンとexec形式・shell形式の使い分けも含めて網羅しています。RHEL 9.4 / Ubuntu 24.04 LTS + Docker 26.x / Compose v2.x で動作確認済みです。

この記事のポイント

・depends_on の condition: service_healthy でDBの準備完了を待ってからアプリを起動できる
・HEALTHCHECK 命令でコンテナの「準備完了」基準を定義する(Dockerfile / Composeファイル)
・curl・nc・独自スクリプトで用途に合わせたヘルスチェックを設計できる
・docker inspect でヘルスチェックのログとExitCodeを確認してデバッグできる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜ起動順序の制御が必要なのか

Docker Compose は depends_on を指定しなければ、すべてのコンテナをほぼ同時に起動します。DBが起動完了する前にアプリが接続を試みると、実際には次のようなエラーが発生します。

app_1 | sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) app_1 | could not connect to server: Connection refused app_1 | Is the server running on host "db" (172.18.0.2) and accepting app_1 | TCP/IP connections on port 5432?

depends_on を指定するだけでは「コンテナが起動した(プロセスが開始した)」ことを待つだけです。PostgreSQLがクライアントからの接続を受け付けるまでには、起動プロセスの完了・WAL再生・リスナー開始など数秒のラグがあります。データ量が多いDBや初回起動時のinitスクリプト実行を含む場合、このラグは10秒以上になることもあります。本番環境でも同様のトラブルが発生しやすい箇所です。

このラグを確実に吸収するのが ヘルスチェック(HEALTHCHECK)condition: service_healthy の組み合わせです。「DBプロセスが起動した」ではなく「DBが接続リクエストに応答できる状態になった」ことを確認してからアプリを起動する仕組みです。

depends_onの基本と3種類のcondition

1. service_started(デフォルト・プロセス起動のみ確認)

condition を省略すると service_started が適用されます。コンテナのプロセスが起動した時点で依存関係を満たしたとみなします。DBが接続を受け付ける前に依存コンテナが起動する可能性があるため、DB待ちには使えません。

# condition を省略した例(service_started と同義) services: app: image: myapp:latest depends_on: - db # プロセス起動のみ確認。DB準備完了は保証しない db: image: postgres:16

2. service_healthy(ヘルスチェック通過を待つ)

最も実用的な設定です。依存先コンテナのヘルスチェックが healthy になるまで待機してから、依存元コンテナを起動します。本番・ステージングを問わず、DBが接続を受け付ける状態になってからアプリを起動したい場合はこれを使います。

services: app: image: myapp:latest depends_on: db: condition: service_healthy # dbがhealthyになるまで起動しない db: image: postgres:16 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5

condition: service_healthy を使うには、依存先コンテナにヘルスチェックが設定されている必要があります。ヘルスチェックがないと service_healthy は永遠に待ち続けます(注意点は後述)。

3. service_completed_successfully(initコンテナの完了を待つ)

DBマイグレーションや初期データ投入など、一度だけ実行して終了するコンテナ(initコンテナ)の完了を待ちたい場合に使います。本番環境でスキーマ変更を伴うデプロイ時に頻繁に使われます。

services: app: image: myapp:latest depends_on: db: condition: service_healthy migrate: condition: service_completed_successfully # マイグレーション完了後に起動 migrate: image: myapp:latest command: python manage.py migrate depends_on: db: condition: service_healthy # マイグレーション自体もDBのhealthy待ち db: image: postgres:16 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5

上記の例では、起動順序は db → migrate → app の順に確実に制御されます。CI/CDパイプラインでのデプロイ自動化にも、そのまま応用できる構成です。

HEALTHCHECK命令でコンテナの「準備完了」を定義する

HEALTHCHECKは「このコンテナが正常に機能している状態とは何か」を定義する仕組みです。ヘルスチェックコマンドが終了コード 0 を返せば「healthy(正常)」、1を返せば「unhealthy(異常)」と判定されます。設定方法は2種類あります。

1. Dockerfileでの設定

イメージに永続的に焼き込む場合は Dockerfile に記述します。独自イメージを作成する際はこちらを使います。

# PostgreSQLイメージに対するHEALTHCHECKの例 FROM postgres:16 # pg_isready でDBが接続を受け付けているか確認 HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=5 CMD pg_isready -U postgres || exit 1

オプションの意味は次のとおりです。

--interval=5s:チェックを5秒ごとに実行する
--timeout=5s:チェックコマンドが5秒以内に完了しない場合はfailとみなす
--start-period=10s:起動後10秒はfailしてもretryカウントに含めない(起動猶予時間)
--retries=5:5回連続でfailしたらunhealthyとみなす

2. docker-compose.ymlのhealthcheckセクションで設定する

Dockerfile を変更できない(公式イメージをそのまま使う)場合は、Composeファイル内で設定します。現場での採用頻度はこちらのほうが高いです。

services: db: image: postgres:16 environment: POSTGRES_USER: appuser POSTGRES_PASSWORD: secret POSTGRES_DB: appdb healthcheck: test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"] interval: 5s timeout: 5s start_period: 10s retries: 5

test に渡す文字列は CMD-SHELL 形式(シェルで実行)と CMD 形式(exec形式)があります。シェルのパイプや条件式を使う場合は CMD-SHELL を選びます。

MySQL と Redis のヘルスチェック例も示します。どちらも実務でよく組み合わせる構成です。

# MySQL 8.0 のヘルスチェック services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: appdb healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-psecret"] interval: 5s timeout: 5s start_period: 30s # MySQLは初期化に時間がかかるため長めに設定 retries: 5 # Redis のヘルスチェック(redis-cli ping が PONG を返せば healthy) services: redis: image: redis:7-alpine healthcheck: test: ["CMD-SHELL", "redis-cli ping | grep PONG"] interval: 5s timeout: 3s start_period: 5s retries: 3

Redis の test にパイプ(|)を使う場合は CMD-SHELL を指定します。redis-cli ping が正常なら PONG を返すため、grep PONG の成否でhealthy/unhealthyを判定できます。

3. exec形式とshell形式の使い分け

Dockerfile の HEALTHCHECK CMD は exec形式(JSON配列)と shell形式(文字列)の2通りで書けます。

# exec形式(JSON配列・シェルを経由しない・推奨) HEALTHCHECK --interval=10s --timeout=5s --retries=3 CMD ["curl", "-fsS", "http://localhost:8080/health"] # shell形式(/bin/sh -c を経由する・パイプを使いたい場合のみ) HEALTHCHECK --interval=10s --timeout=5s --retries=3 CMD redis-cli ping | grep PONG

exec形式はシェルを経由しないため、/bin/sh が存在しない distroless イメージや slim 系イメージでも動作します。パイプ(|)や条件演算子(&&||)を使う必要がある場合のみ shell形式を選んでください。

終了コードのルールは次のとおりです。

・終了コード 0:healthy(正常)
・終了コード 1:unhealthy(異常)
・終了コード 2:Docker仕様で「予約済み」のため使用しない(将来の拡張用)

4. HEALTHCHECKを無効化する方法

ベースイメージがすでに HEALTHCHECK を設定している場合、子イメージでそれを無効化できます。デバッグ時や特定の環境でヘルスチェックを意図的に外したい場合に使います。

# 親イメージのHEALTHCHECKを無効化する FROM mybaseimage:latest HEALTHCHECK NONE

HEALTHCHECK NONE を指定すると、ベースイメージに設定されたヘルスチェックが子イメージで完全に無効化されます。docker ps の STATUS 列にはヘルスチェック情報が表示されなくなります。

実践例:PostgreSQL + Webアプリの起動順序を制御する

1. Composeファイルの全体構成

PostgreSQL と FastAPI(Python)アプリを組み合わせた構成例です。開発・本番環境でそのまま使える構成を示します。

# docker-compose.yml services: db: image: postgres:16 environment: POSTGRES_USER: appuser POSTGRES_PASSWORD: secret POSTGRES_DB: appdb healthcheck: test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"] interval: 5s timeout: 5s start_period: 10s retries: 5 volumes: - pgdata:/var/lib/postgresql/data app: build: . ports: - "8000:8000" environment: DATABASE_URL: "postgresql://appuser:secret@db:5432/appdb" depends_on: db: condition: service_healthy # dbがhealthyになってから起動 restart: on-failure # 接続失敗時にリスタート volumes: pgdata:

restart: on-failure は、ヘルスチェック通過後に万が一DB接続に失敗した場合の保険です。

2. ヘルスチェックの動作確認

docker compose up を実行すると、DBコンテナのヘルスチェックが通過するまで app コンテナの起動が待機されます。

$ docker compose up -d [+] Running 2/2 ✔ Container sample-db-1 Healthy 6.5s ✔ Container sample-app-1 Started 6.8s $ docker compose ps NAME IMAGE STATUS PORTS sample-app-1 myapp:latest Up 10 seconds 0.0.0.0:8000->8000/tcp sample-db-1 postgres:16 Up 15 seconds (healthy) 5432/tcp

「Healthy 6.5s」の数字はDBがhealthyになるまでの待機時間です。この間、app コンテナは起動を保留されています。docker compose ps の STATUS 列には次の3種類の状態が表示されます。

(starting)--start-period の猶予期間中か、初回チェック待ち
(healthy):直近のチェックが成功した状態
(unhealthy)--retries 回連続してチェックが失敗した状態

(healthy) と表示されていれば、起動順序の制御が正常に機能しています。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Docker Composeの設計パターンからコンテナ本番運用まで含めた実践的なDocker講座を用意しています。
Dockerマスター講座の詳細はこちら >>

ヘルスチェックが unhealthy になった場合のデバッグ手順

1. docker inspect でステータスとログを確認する

docker inspect を使うと、ヘルスチェックの詳細な実行履歴が取得できます。障害発生時の第一調査コマンドとして覚えておいてください。

# コンテナ名を確認 $ docker compose ps # ヘルスチェック詳細を取得 $ docker inspect sample-db-1 | python3 -m json.tool | grep -A 20 '"Health"' # 出力例(失敗している場合) "Health": { "Status": "unhealthy", "FailingStreak": 3, "Log": [ { "Start": "2026-07-07T08:00:01.234Z", "End": "2026-07-07T08:00:01.239Z", "ExitCode": 1, "Output": "pg_isready: error: connection to server at localhost failed" } ] }

Output フィールドにヘルスチェックコマンドの標準エラーが出力されます。このメッセージを起点に原因を特定します。

ExitCode の値は次のように解釈します。

ExitCode: 0:healthy(チェック成功)
ExitCode: 1:unhealthy(チェックコマンドがエラーを返した)
ExitCode: 124:タイムアウト(--timeout 内にコマンドが応答しなかった)

ExitCode が 124 の場合は --timeout の値を延ばすか、ヘルスチェックコマンド自体の応答速度を確認してください。

2. よくある原因と対処

pg_isreadyのユーザー名が違う-U オプションで指定するユーザー名が POSTGRES_USER と一致しているか確認
start_period が短すぎる:大きなDBは初期化に時間がかかるため、start_period: 30s 程度に延ばす
ネットワーク名前解決の失敗:ヘルスチェックはコンテナ自身の内部で実行されるため、localhost または 127.0.0.1 を使う(サービス名は使えない)
testコマンドがコンテナ内に存在しない:Alpine や slim 系イメージには curl が入っていないことがある。その場合は nc や専用CLI(pg_isreadymysqladmin pingredis-cli ping)で代替する

curlを使ったWebサーバーのヘルスチェック例と、curlが使えない場合のncによる代替例を示します。

# curlによるHTTPエンドポイントの確認(-f: 4xx/5xxで終了コード22を返す) healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 10s timeout: 5s start_period: 15s retries: 3 # curlがない場合はncでTCPポート確認(-z: 接続のみ -w 1: 1秒タイムアウト) healthcheck: test: ["CMD", "nc", "-z", "-w", "1", "localhost", "8080"] interval: 10s timeout: 5s start_period: 15s retries: 3

curl-f オプションは HTTP 4xx/5xx レスポンスを受け取ったときに終了コード 22 を返すため、アプリがエラーレスポンスを返している状態も unhealthy として検出できます。

3. ヘルスチェックが永遠に待ち続ける場合

condition: service_healthy を設定したのに依存先コンテナに healthcheck: が定義されていない場合、Docker Composeはタイムアウトなしで無限に待ち続けます。docker compose up が一向に完了しない場合はこれが原因の場合があります。

# ヘルスチェックが設定されているか確認する $ docker inspect sample-db-1 --format='{{json .Config.Healthcheck}}' # 設定なしの場合(healthcheckを追加する必要がある) null # 設定ありの場合 {"Test":["CMD-SHELL","pg_isready -U appuser -d appdb"],"Interval":5000000000,"Timeout":5000000000,"StartPeriod":10000000000,"Retries":5}

Interval などの数値はナノ秒単位です(例:5000000000ns = 5秒)。null が返った場合は healthcheck セクションを追加してください。

depends_onとヘルスチェックを使う時の注意点

ヘルスチェックは「起動の遅延」であり「接続の保証」ではありません。(要注意)

ヘルスチェック通過後も一時的にDBへの接続が失敗するケース(高負荷・ネットワーク瞬断)は現場で実際に起こります。アプリ側にも接続リトライ処理を実装するのが運用の鉄則です。

PythonのSQLAlchemyであれば pool_pre_ping=True を設定することで、使用前に接続の生存確認が行われます。

# SQLAlchemy の接続リトライ設定例 from sqlalchemy import create_engine engine = create_engine( "postgresql://appuser:secret@db:5432/appdb", pool_pre_ping=True, # 使用前に接続確認 pool_recycle=3600, # 1時間で接続を再作成 connect_args={"connect_timeout": 10} )

また、Compose v2.20以降では depends_onrequired フィールドで依存サービスが起動できなかった場合の挙動を制御できます。required: false を指定すると、依存先が起動失敗してもアプリを起動させることができます。Redisが落ちてもアプリ本体は動作させたいといったグレースフルデグレードの設計に活用します。

services: app: image: myapp:latest depends_on: db: condition: service_healthy required: true # dbがないと起動しない(デフォルト) redis: condition: service_healthy required: false # redisが起動できなくてもappは起動する db: image: postgres:16 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 redis: image: redis:7-alpine healthcheck: test: ["CMD-SHELL", "redis-cli ping | grep PONG"] interval: 5s timeout: 3s retries: 3

本記事のまとめ

Docker Compose の起動順序制御について、depends_on の3種類のconditionとHEALTHCHECKの設定方法を解説しました。

やりたいこと 設定
DBの準備完了を待ってアプリを起動する condition: service_healthy
DBのヘルスチェックを定義する Composefile の healthcheck セクション
initコンテナの完了を待つ condition: service_completed_successfully
ヘルスチェック状態を確認する docker inspect コンテナ名
タイムアウト(ExitCode 124)を調べる --timeoutの値を延ばして再確認する
接続リトライをアプリ側で対処する SQLAlchemy の pool_pre_ping=True
依存サービスが落ちてもアプリを起動する required: false(Compose v2.20以降)
親イメージのHEALTHCHECKを無効化する HEALTHCHECK NONE
・ヘルスチェックは「DBが接続を受け付けているか」を確認するコマンドを test に指定する
start_period で起動猶予時間を設ける(DBの初期化時間に合わせて調整する)
・curl が使えないイメージは nc または専用CLI(pg_isreadymysqladmin pingredis-cli ping)で代替する
・ヘルスチェック通過はあくまで「起動の遅延」。アプリ側のリトライ処理も必ず実装する

次に読む記事

DockerのENV・ARG・env_fileを正しく使う設計|ビルド時と実行時の値の渡し方とsecrets
DockerfileのENTRYPOINTとCMDを正しく使い分ける方法|シェル形式・exec形式の設計指針
Docker Compose入門|複数コンテナでWordPress環境を構築するハンズオン

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Docker Composeの起動順序設計からコンテナ本番運用まで体系的に学べるDocker講座を用意しています。
Dockerマスター講座の詳細はこちら >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。