サーバー ソフトウェア 公開日 2026.04.14 更新日 2026.06.13

Docker Composeとは?複数コンテナをまとめて動かす基本を初心者向けに解説

Docker Compose とは何かを、なぜ複数コンテナで便利なのか、docker run と何が違うのか、最小の compose.yaml で何を定義するのかまで初心者向けに整理した記事です。

先に要点

  • Docker Compose は、複数の コンテナ をまとめて定義・起動しやすくする仕組みです。
  • アプリ + DB + Redis のように複数サービスを一緒に立ち上げたいときにかなり便利です。
  • docker run を何回も打つより、構成をファイルに残して再現しやすくできます。
  • 最初は services ports volumes environment あたりを読めれば十分です。ただし depends_on だけでは「DB が接続を受け付ける状態」までは待たないので、ここは早めに正しく押さえます。

Docker は何となく分かったけど、Compose は何が違うの?」で止まる人は多いです。 実際、単体コンテナだけなら docker run でも動くので、わざわざ別の仕組みが必要に見えにくいです。

でも実務では、アプリ単体で終わることはあまりありません。 DBRedis、メール確認用ツール、ジョブワーカーなど、複数サービスをまとめて動かしたい場面がかなり多いです。

この記事では、Docker Docs の Compose ドキュメントと Startup order の公開情報を確認しながら、Docker Compose とは何か、何が便利なのか、初心者が最初にどこまで理解すればよいかを整理します。特に「depends_on を書いたのにアプリが起動直後に DB 接続で落ちる」という、ほぼ全員が一度は踏む失敗を、実際のエラーログと直し方つきで本文に入れています。 Docker 自体の意味から先に押さえたいなら、Dockerとは?コンテナで何がうれしい?初心者向けに仕組み・メリット・使いどころを解説 から読むと流れがつかみやすいです。


Docker Composeとは何か

Docker Compose は、複数コンテナの構成をファイルにまとめて、まとめて起動・停止しやすくする仕組みです。 初心者向けにかなりざっくり言うと、「複数の docker run を、分かりやすい設定ファイルにしたもの」と考えると入りやすいです。

たとえば Web アプリを動かすだけでも、

が必要になることがあります。 これを毎回手で順番に立ち上げるのは面倒ですし、チーム内で同じ条件をそろえるのも大変です。

Compose を使うと、「このプロジェクトは何を何と一緒に起動するか」を compose.yaml(YAML 形式)に残せます。 その結果、再現性がかなり上がります。


何がうれしいのか

1. 複数サービスをまとめて起動できる

これがいちばん分かりやすいです。 アプリと DB を別々に考えるのではなく、「この開発環境一式」としてまとめて扱えます。

たとえば、

を使う Laravel 案件なら、1つずつコンテナを起動するより Compose でまとめた方がかなり楽です。

2. 構成がファイルに残る

「誰かのPCでは動いているけど、どう起動したか分からない」は地味にしんどいです。 Compose なら、ポート、環境変数、ボリューム、依存関係がファイルに残るので、あとから見返しやすいです。

3. チームで共有しやすい

README に長い起動手順を書くより、docker compose up -d で近い状態まで持っていける方が入りやすいです。 新しく参加した人が、ローカル環境で詰まりにくくなるのはかなり大きいです。

4. 検証環境を作り直しやすい

Compose は、「壊れたらもう一回立てる」発想と相性がいいです。 もちろん永続データはボリュームの扱いに注意が必要ですが、少なくとも開発用の複数サービス構成はかなり戻しやすいです。

向いている場面

ローカル開発、CI のテスト環境、小規模 VPS での単一ホスト運用。アプリと DBRedis を「一式」で起動・破棄したいときに強いです。

向いていない場面

複数ホストへの分散や自動スケール、ローリング更新が前提の本番。ここは KubernetesAWS ECSCloud Run などの出番です。


docker run と何が違うのか

docker run は単体コンテナを動かすには十分便利です。 ただ、複数サービスになると、だんだん管理がつらくなります。

観点 docker run Docker Compose
向いている場面 単体コンテナの試行 複数サービス構成の管理
設定の残しやすさ コマンド履歴頼りになりやすい compose.yaml に残せる
チーム共有 ややしづらい かなりしやすい
起動停止 個別操作が増える まとめて扱いやすい
起動順・依存 自分で順番を管理 depends_on で宣言できる(ただし後述の注意あり)

要するに、1個だけ試すなら docker run でもよいですが、「プロジェクト構成として残したい」なら Compose の方がかなり向いています。


最小の compose.yaml で何を書くのか

最初は全部理解しなくて大丈夫です。 まずは次の項目だけ見ればかなり足ります。

  • services: どのサービスを立てるか
  • image または build: 何からコンテナを作るか
  • ports: どのポートを外へ開けるか
  • volumes: どこを永続化するか
  • environment: 環境変数

たとえば、かなり単純化するとこんな形です。

services:
  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: app
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:

この例で見たいのは、文法の細かさより「app と db を一緒に定義している」ことです。 ここが Compose の本質にかなり近いです。

ただし、この depends_on の書き方には大きな落とし穴があります。次の章でそれを実際のエラーつきで見ていきます。


いちばん多い失敗: depends_on だけだとアプリが DB 起動前に接続失敗する

Compose を触り始めて最初にハマるのが、ほぼこれです。 depends_on: - db と書いてあるのに、docker compose up した瞬間に app コンテナDB 接続エラーで落ちる、という現象です。

現象

上の compose.yaml のまま起動すると、app のログにこんな出力が出て、app だけがすぐ終了します。

PostgreSQL を使っている場合の典型はこれです。

app-1  | psycopg2.OperationalError: 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?
app-1 exited with code 1

MySQL の場合はこういう形になります。

app-1  | SQLSTATE[HY000] [2002] Connection refused
app-1  | (or) Can't connect to MySQL server on 'db' (111)
app-1 exited with code 1

原因

depends_on が保証するのは「コンテナの起動順」だけです。Docker Docs の Startup order にもはっきり書かれていて、「Compose はコンテナが running になるまでしか待たず、ready(接続を受け付けられる状態)になるまでは待たない」という挙動です。

MySQLPostgreSQL は、コンテナのプロセス自体は数百ミリ秒で立ち上がりますが、その後に内部で初期化(初回なら DB の作成、データディレクトリの整備など)を走らせます。この初期化が終わってポートで接続を受け付けるまで、初回起動だと数秒から十数秒かかります。 一方 app は db が「running」になった瞬間に起動して、すぐ接続しにいきます。その時 DB はまだポートを開いていないので、Connection refused が返ってくる、というタイミングのズレが原因です。

確認手順

本当に「順番は合っているが待てていない」のかを切り分けます。

読み込み中...

docker compose ps の出力イメージはこうです。app だけが落ちているのが分かります。

NAME     IMAGE        STATUS                     PORTS
app-1    app:latest   Exited (1) 2 seconds ago
db-1     mysql:8.4    Up 3 seconds               3306/tcp

回避: healthcheck + condition: service_healthy

正攻法は、DB 側に healthcheck を定義し、app 側の depends_on で「healthy になるまで待つ」と宣言することです。これでタイミング問題は根本から消えます。

services:
  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: app
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 30s

volumes:
  db-data:

PostgreSQL なら DB 付属の pg_isready を使うのが定番です。

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

各フィールドの意味を押さえておくと調整しやすいです。

フィールド 役割 目安
test healthy 判定に使うコマンド。終了コード 0 で healthy pg_isready / mysqladmin ping など
interval チェックの間隔 5s〜10s
timeout 1回のチェックがこの時間を超えたら失敗扱い 3s〜10s
retries 連続失敗がこの回数に達したら unhealthy 5〜10
start_period 起動直後の猶予。この間の失敗は retries に数えない 初期化が重い DB は 30s 前後

直ったかどうかは docker compose ps の health 表示と、docker inspect のログで確認できます。

$ docker compose ps
NAME    IMAGE        STATUS
app-1   app:latest   Up 12 seconds
db-1    mysql:8.4    Up 15 seconds (healthy)

$ docker inspect --format '{{json .State.Health}}' db-1
{"Status":"healthy","FailingStreak":0,"Log":[{"ExitCode":0,"Output":"mysqld is alive"}]}

db が (healthy) になってから app が起動し、app のログに Connection refused が出なくなれば成功です。

補足: アプリ側のリトライも持っておくと強い

healthcheck はかなり効きますが、本番では DB の再起動や瞬断もあり得ます。アプリ側にも接続リトライ(数秒おきに数回再接続)を入れておくと、Compose 環境でも本番環境でも落ちにくくなります。condition: service_healthyrestart: true を添えると、DB が再起動したとき依存サービスも再起動させられます。


実務でよくある使い方

ローカル開発環境をそろえる

いちばん多い入口です。 特に LaravelDjango、Node.js のように、アプリと DB をセットで動かしたい構成ではかなり相性がいいです。

ステージングで軽く再現する

本番そのものではなくても、開発チーム内で近い構成を簡単に再現したい場面があります。 このとき Compose は扱いやすいです。

VPS で複数サービスをまとめて管理する

小規模構成では、Compose でアプリと周辺サービスをまとめて管理することもあります。 VPS まわりまでつなげて見たいなら、クラウド、VPS、レンタルサーバーの違いは?コスパ比較と実務での使い分けを解説Linuxサーバーの初期設定で最初にやることは?見直したい項目を実務目線で整理 もつながります。

実務での使用例

ローカルの Laravel 検証環境で、app / nginx / mysql / redis を Compose でまとめる形はかなり現実的です。アプリ担当が増えても同じ構成を渡しやすく、「ローカルだけ Redis が無い」みたいなズレも減らしやすいです。ここでも mysql に healthcheck を入れておけば、初回の docker compose up でマイグレーションが Connection refused で失敗する事故を防げます。


初心者が最初につまずきやすい点

depends_on があれば全部安心だと思ってしまう

これは前章で詳しく扱ったとおりです。depends_on: - db だけでは「DB が起動した」までしか待たず、「接続を受け付けられる」までは待ちません。healthcheck + condition: service_healthy までセットで初めて安心できます。

ボリュームを理解しないまま使う

DB のデータを永続化したいのに、ボリュームなしで作ると、コンテナを消したときに中身も消えやすいです。 逆に、開発用なのに古いボリュームが残っていて、状態が変に引き継がれることもあります。古い状態を完全に消したいときは docker compose down -vボリュームごと破棄します(本番では取り扱い厳重注意)。

何でも1ファイルに詰め込む

Compose は便利ですが、サービスが増えるほど設定も膨らみます。 最初から全部入りにせず、まずは本当に必要なサービスだけで始める方が分かりやすいです。


まずどう触ればいいか

最初は次の順で十分です。

読み込み中...

この順なら、いきなり複雑な YAML に飲まれにくいです。 Compose は「全部の機能を覚えてから使う」より、「必要な2〜3項目だけで実際に動かす」方が理解しやすいです。


Docker Composeに関するよくある質問

Q. docker compose と docker-compose はどちらを使うべきですか?

A. 新しいプロジェクトでは docker compose(スペース付き、Docker CLI 統合)を使います。docker-compose(ハイフン付き、Compose V1)は 2023 年で開発終了しています。

Q. depends_on を書いたのにアプリが DB 接続でクラッシュします。

A. これはこの記事の中心トピックです。depends_on はコンテナの起動順しか保証せず、DB が接続を受け付ける状態までは待ちません。DB 側に healthcheck(pg_isreadymysqladmin ping)を定義し、app 側で depends_on: db: condition: service_healthy と書くことで、Connection refused を解消できます。

Q. healthcheck の start_period は何のためにありますか?

A. 起動直後の猶予時間です。この期間に失敗しても retries にカウントされません。DB の初回初期化のように立ち上がりが重いものは、ここを 30s 前後にしておくと、起動途中の一時的な失敗で unhealthy 扱いされずに済みます。

Q. healthcheck が効いているか確認する方法は?

A. docker compose ps で対象サービスが (healthy) 表示になるかを見ます。詳細は docker inspect --format '{{json .State.Health}}' <container> で、各チェックの ExitCode と出力ログを確認できます。

Q. Docker Compose は本番でも使えますか?

A. 単一ホストで動かす本番なら使えます。複数ホストや高可用性が必要なら KubernetesAWS ECSCloud Run などに移行します。本番初期 → 大規模、への段階的移行がよくあるパターンです。

Q. データベースのデータを永続化するには?

A. volumes でホストのディレクトリまたは Docker 管理ボリュームにマウントします。db-data:/var/lib/mysql のように書いておけば、docker compose down してもデータが残ります(down -v すると消えます)。

Q. 複数の Compose プロジェクトを同時に起動するとポートが競合します。どうすれば?

A. ports127.0.0.1:8081:80 のように IP 指定する、プロジェクトごとに別ポートを使う、または docker compose -p でプロジェクト名を分けるのが定番です。

Q. 開発用と本番用で Compose ファイルを分けたいです。

A. compose.override.yaml を使うと、開発時の差分(ボリュームマウント、ポート公開、デバッグ設定)を別ファイルに分けられます。本番用は compose.prod.yaml のように明示的に -f で指定する形が多いです。


まとめ

Docker Compose は、複数の コンテナ をまとめて定義・起動しやすくする仕組みです。 特に アプリ + DB + Redis のような構成ではかなり便利で、設定をファイルに残せるので再現性も上げやすいです。

単体コンテナだけなら docker run でも十分ですが、プロジェクト構成として管理したいなら Compose の方がかなり分かりやすいです。 最初は services ports volumes environment だけ読めれば十分ですが、depends_on だけでは DB の準備完了を待てない点だけは早めに押さえ、healthcheck + condition: service_healthy をセットで使えるようにしておくと、起動直後の Connection refused でつまずかずに済みます。

続けて読むなら、Dockerとは?コンテナで何がうれしい?初心者向けに仕組み・メリット・使いどころを解説Dev Containersとは?ローカル開発を汚さない開発環境の作り方を初心者向けに解説 もかなり相性がいいです。


参考リンク

あとで見返すならここで保存

読み終わったあとに残しておきたい記事は、お気に入りからまとめて辿れます。