公開日 2026-05-28

Docker初心者向け: compose.yml と Dockerfile の違いを最小構成で理解する

compose.yml と Dockerfile の責務分担を最小 PHP サンプルで整理し、ポート・環境変数・マウント・PHP拡張をどちらへ書くか判断できるようにする。

目次

  1. 1. ゴールと非対象
  2. 2. まず結論: Dockerfile はイメージ、compose.yml は起動方法
  3. 3. 最小構成を先に見る
  4. 3-1. compose.yml を作成する
  5. 3-2. docker/php/Dockerfile を作成する
  6. 3-3. app/index.php を作成する
  7. 4. Dockerfile は何を決めるか
  8. 5. compose.yml は何を決めるか
  9. 6. どちらに書くべきかを具体例で整理する
  10. 7. 既存記事を読むときの見方
  11. 8. よくある勘違い
  12. Dockerfile にポート公開を書くと思ってしまう
  13. COPY したからローカル修正がすぐ反映されると思ってしまう
  14. build があるから compose.yml がイメージの中身も決めているように見える
  15. 起動コマンドを変えるたびに Dockerfile を直してしまう
  16. 9. まとめ

Windows 11で始めるPHPローカル開発環境WSL2 + Docker + PHP + PostgreSQLで最小CRUDを作る のような Docker 前提記事へ入る前に、どちらに何を書くかだけ先に整理するための補助記事です。 この 1 本では app 1 サービスの最小 PHP サンプルを使い、Dockerfile がイメージの中身、compose.yml がコンテナの起動方法を決める流れを切り分けます。Compose キーや Dockerfile 命令の全網羅、本番運用、Kubernetes は非対象です。

1. ゴールと非対象

到達する状態:

  • PHP 拡張やベースイメージを変えたいときに Dockerfile を見る
  • ポート公開、環境変数、マウント、起動コマンドを変えたいときに compose.yml を見る
  • 既存記事のサンプルを読んだとき、どのファイルを直すべきか当たりを付けられる

扱わない内容:

  • Docker の歴史や内部実装の深掘り
  • Compose キーや Dockerfile 命令の全網羅
  • 本番向けのセキュリティ設定やマルチステージビルド
  • Kubernetes や複数サービス構成の設計論

Docker 全体を 1 回で覚える記事ではありません。 役割分担だけを先に固め、後続の記事を読みやすくすることに絞ります。

2. まず結論: Dockerfile はイメージ、compose.yml は起動方法

先に結論を 1 文で置くと、Dockerfile は「イメージの中身を決めるファイル」、compose.yml は「そのイメージをどう起動するかを決めるファイル」です。

迷ったときの切り分けはシンプルです。 それがビルド時に固定されることなら Dockerfile、起動時に差し替えたいことなら compose.yml を見ます。

やりたいことまず見るファイル理由
PHP 拡張や OS パッケージを足したいDockerfileイメージの中身を build 時に変えるため
ベースイメージを変えたいDockerfileどの土台でコンテナを作るかを決めるため
公開ポートを変えたいcompose.ymlホストとコンテナのつなぎ方は起動時の設定だから
ソースコードをマウントしたいcompose.ymlホスト側のパスを bind mount する設定だから
環境変数を渡したいcompose.yml実行環境ごとに差し替えやすいから
起動コマンドだけ変えたいcompose.ymlCMD を上書きして試せるから

関係を図にすると次の並びです。

flowchart LR
    A["app/index.php"] -->|"COPY (build time)"| C["image"]
    B["Dockerfile"] -->|"docker build"| C
    D["compose.yml"] -->|"ports / volumes / environment"| E["container"]
    C --> E
    A -->|"bind mount (run time)"| E

Dockerfile だけではコンテナは起動しません。 compose.yml だけでは、イメージの中身は作れないままです。 2 つを分けて読むと、既存構成の修正箇所が見えやすくなります。

3. 最小構成を先に見る

本文では compose.yml 表記で統一します。 古い記事や既存リポジトリでは docker-compose.yml を見かけますが、役割の見方は同じです。

最小構成は 3 ファイルだけです。

docker-compose-vs-dockerfile-demo/
├─ compose.yml
├─ docker/
│  └─ php/
│     └─ Dockerfile
└─ app/
   └─ index.php

作業ディレクトリを作って開きます。

mkdir -p ~/projects/docker-compose-vs-dockerfile-demo/app
mkdir -p ~/projects/docker-compose-vs-dockerfile-demo/docker/php
cd ~/projects/docker-compose-vs-dockerfile-demo
code .

3-1. compose.yml を作成する

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    ports:
      - "8080:80"
    volumes:
      - ./app:/var/www/html
    environment:
      APP_MESSAGE: "Hello from compose.yml"

3-2. docker/php/Dockerfile を作成する

FROM php:8.5-apache

WORKDIR /var/www/html

COPY app/ /var/www/html/

CMD ["apache2-foreground"]

3-3. app/index.php を作成する

<?php

declare(strict_types=1);

$message = getenv('APP_MESSAGE') ?: 'Hello from Dockerfile';

echo "<h1>{$message}</h1>";
echo '<p>PHP version: ' . PHP_VERSION . '</p>';

起動確認は最小限で十分です。

docker compose config
docker compose up -d --build
curl http://localhost:8080

Hello from compose.yml と PHP バージョンが表示されれば成功です。

curl実行結果

このサンプルで押さえたい点は 2 つあります。

  • DockerfileCOPY で、build 時点の app/ をイメージへ入れている
  • compose.ymlvolumes で、起動時にはホスト側の ./app をその場所へマウントしている

ローカル開発では volumes のほうが実行中コンテナの見え方を左右します。 そのため、ソースを直したいだけなら compose.yml 側の bind mount を見るのが近道です。

4. Dockerfile は何を決めるか

Dockerfile は、これから作るイメージの中身を上から順に定義します。 今回の最小例なら、読めば十分です。

FROM php:8.5-apache

WORKDIR /var/www/html

COPY app/ /var/www/html/

CMD ["apache2-foreground"]

各行の役割:

  • FROM php:8.5-apache
    • どのイメージを土台にするかを決めます。PHP の種類や OS 側の前提はここから始まります。
  • WORKDIR /var/www/html
    • 以後の作業場所をコンテナ内のどこに置くかの指定です。docker compose exec app pwd で入ったときの基準点にもなります。
  • COPY app/ /var/www/html/
    • build 時点のホストファイルをイメージへコピーする命令です。bind mount を使わない環境でも、イメージだけで最低限動く形を作れます。
  • CMD ["apache2-foreground"]
    • コンテナ起動時の既定コマンドです。毎回の起動で同じ既定値にしたいなら、ここへ書きます。

今回の最小例では RUN を省いています。 理由は、責務比較の主役が Dockerfilecompose.yml の切り分けだからです。

PHP 拡張や OS パッケージを入れたいなら、追加先は Dockerfile です。 たとえば今の Dockerfile に次を COPY の前へ足すと、PostgreSQL 用の拡張を build 時に組み込めます。

RUN apt-get update \
    && apt-get install -y --no-install-recommends libpq-dev \
    && docker-php-ext-install pdo_pgsql \
    && rm -rf /var/lib/apt/lists/*

ここでやっているのは、起動時の一時設定ではなくイメージの変更です。 だから compose.yml ではなく Dockerfile へ書きます。

5. compose.yml は何を決めるか

compose.yml は、作ったイメージをどう起動するかをまとめるファイルです。 単一サービスでも役割ははっきりしています。

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    ports:
      - "8080:80"
    volumes:
      - ./app:/var/www/html
    environment:
      APP_MESSAGE: "Hello from compose.yml"

各項目の見方:

  • services
    • 何個のコンテナをどういう名前で扱うかを決めます。今回は app 1 つだけです。
  • build
    • どのディレクトリを build context にして、どの Dockerfile を使うかの指定です。ここは「Dockerfile を使う入口」であって、イメージの中身そのものを書く場所ではありません。
  • ports
    • ホストの 8080 をコンテナの 80 へつなぐ設定です。公開ポートを変えたいときはここを直します。
  • volumes
    • ホストの ./app をコンテナの /var/www/html へ bind mount する設定です。ローカルでソース変更をすぐ反映したいときに使います。
  • environment
    • 実行時に環境変数を渡します。今回の APP_MESSAGE の受け取り元はここです。

commandcompose.yml 側の役割です。 これは DockerfileCMD を起動時だけ上書きするための設定で、環境ごとに起動方法を変えたい場面で使います。

既定値として全環境で持たせたいなら DockerfileCMD、特定の環境や一時的な上書きなら compose.ymlcommand と覚えておくと迷いにくくなります。

6. どちらに書くべきかを具体例で整理する

ここが実務で最も役立つ部分です。 「やりたいこと」と「その理由」を短く結びます。

やりたいこと書く場所理由
PHP 拡張を入れたいDockerfilebuild したイメージへ組み込む変更だから
ベースイメージを php:8.5-apache から別のものへ変えたいDockerfileどの土台でコンテナを作るかを固定するため
作業ディレクトリを変えたいDockerfileコンテナ内の既定の作業場所を変えるため
localhost:8080 ではなく localhost:9000 で開きたいcompose.ymlホスト側のポート公開は起動時設定だから
ホストのソースコードをそのまま反映したいcompose.ymlbind mount は実行中コンテナへの接続設定だから
.env や環境ごとに値が違う環境変数を渡したいcompose.yml実行環境ごとに差し替えやすいから
起動コマンドだけを一時的に変えたいcompose.ymlcommandDockerfile を rebuild しなくても CMD を上書きできるから
全環境で共通の既定コマンドにしたいDockerfileCMDイメージの既定動作として持たせたいから

迷ったら、次の 2 問です。

  1. その変更は build し直さないと反映されないか
  2. 環境ごとに差し替えたい設定か

1 つ目が yes なら Dockerfile、2 つ目が yes なら compose.yml を疑うと整理しやすくなります。

7. 既存記事を読むときの見方

このブログの Docker 前提記事を読むときも、順番は同じです。

  1. まず compose.yml を開く
  2. services の数、portsvolumesenvironment を見る
  3. build があれば、その先の Dockerfile を開く
  4. command があれば DockerfileCMD を上書きしていないか確認する

この順で読むと、構成の全体像と修正場所が分かれます。 逆順で Dockerfile から入ると、サービス数や公開ポートを見落としやすい流れです。

次に読む候補:

8. よくある勘違い

Dockerfile にポート公開を書くと思ってしまう

Dockerfile にも EXPOSE は書けますが、ホストのどのポートで受けるかまでは決まりません。 localhost:8080 のような公開設定は compose.ymlports で決めます。

COPY したからローカル修正がすぐ反映されると思ってしまう

COPY は build 時点のファイルをイメージへ入れる命令です。 ローカル開発でファイル変更を即反映したいなら、見るべきなのは compose.ymlvolumes です。

build があるから compose.yml がイメージの中身も決めているように見える

build は「この Dockerfile を使って build する」という入口にすぎません。 何をインストールし、何をコピーし、どの CMD を既定にするかは Dockerfile 側で決まります。

起動コマンドを変えるたびに Dockerfile を直してしまう

全環境で同じ既定値に変えたいなら CMD を直す価値があります。 一方で、開発環境だけ少し変えたいなら compose.ymlcommand で十分です。

9. まとめ

Dockerfile はイメージの中身、compose.yml はコンテナの起動方法です。 この 2 つを分けて読むだけで、既存の Docker 構成は追いやすくなります。

次に既存記事を見るときは、まず compose.ymlservicesports を見てから、build 先の Dockerfile を開いてください。 その順番が定着すると、「PHP 拡張を足したいのに compose.yml を触っていた」「ポートを変えたいのに Dockerfile を見ていた」という迷いが減ります。

シリーズ 1/3

このシリーズ

Docker設定ファイルを上から読む

  1. 1. Docker初心者向け: compose.yml と Dockerfile の違いを最小構成で理解する 現在の記事
  2. 2. Docker初心者向け: compose.yml の中身を1項目ずつ読む入門――services / ports / volumes / environment
  3. 3. Docker初心者向け: Dockerfile の中身を上から読む――FROM / WORKDIR / COPY / RUN / CMD