Windows 11でDocker Composeを使ってLaravel環境を作る:Container・Volume・Networkの基本
作成日:2026.10.01
Windows 11のDocker Desktop上に、DockerfileとDocker Composeを使ってLaravel・PHP・MySQL・Mailpitの開発環境を構築します。ImageとContainerの関係、Bind MountとNamed Volumeによるデータの保存、ポート公開とContainer間通信の違いを実例で確認。LaravelからMySQLへ接続するときに、localhostではなくService名を使う理由も説明します。
目次
Dockerを使い始めると、Image、Container、Volume、Networkなどの用語が出てきます。今回はWindows 11のDocker Desktop上にLaravel、PHP、MySQL、Mailpitの開発環境を作りながら、それぞれの役割を確認します。
Laravel Sailは使わず、Dockerfileとcompose.yamlを用意します。特に、Containerを削除したときにデータがどうなるか、LaravelからMySQLへ接続するときにlocalhostではなくService名を使う理由を見ていきます。
今回の環境
対象はWindows 11、Docker DesktopのWSL2バックエンドです。手元の環境では、次のバージョンを使いました。
Docker version 29.8.1
Docker Compose version v5.5.1
PHP 8.3.35
Composer 2.10.3
MySQL 8.0
Docker Desktopをインストールした後、PowerShellでDocker CLIとComposeを確認します。
docker --version
docker compose version
docker run hello-world
hello-worldの実行まで完了すれば、Docker Engineが起動し、Imageの取得とContainerの実行ができています。Docker Desktopのインストール条件やWSL2の設定は、Docker DesktopのWSL2バックエンドに関する公式資料を確認してください。
ImageとContainerの関係
Imageは、Containerを作るときに使うファイルや設定をまとめたものです。ContainerはImageをもとに起動する実行単位です。
Dockerfile
↓ build
Image
↓ run
Container
たとえば、PHPの公式Imageを土台にPHP拡張やComposerを追加したものをImageとして作り、そのImageからLaravelを実行するContainerを起動できます。ImageとContainerの詳しい説明は、Docker公式ドキュメントのImageの説明とContainerの説明も参照してください。
PHP用ImageとComposeの設定を作る
作業用ディレクトリを用意し、Docker関連のファイルとLaravel本体を分けます。
New-Item -ItemType Directory -Path D:\dev\docker-laravel-study\src -Force
Set-Location D:\dev\docker-laravel-study
まず、PHP用Imageを作るDockerfileを作成します。
FROM php:8.3-cli
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git \
unzip \
libzip-dev \
libonig-dev \
&& docker-php-ext-install \
pdo_mysql \
mbstring \
zip \
&& rm -rf /var/lib/apt/lists/*
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
php:8.3-cliはPHP公式ImageのCLI版です。MySQL接続用のpdo_mysql、Laravelで使うmbstring、Composer依存関係で使うzip拡張を追加しています。公式PHP Imageには拡張を有効化するためのdocker-php-ext-installなどの補助スクリプトがあります。詳細はPHP公式Docker Imageの説明を参照してください。
次に、Laravel、MySQL、Mailpitをまとめて起動するcompose.yamlを作ります。
services:
app:
build:
context: .
working_dir: /var/www/html
volumes:
- ./src:/var/www/html
ports:
- "127.0.0.1:8000:8000"
command: php artisan serve --host=0.0.0.0 --port=8000
depends_on:
db:
condition: service_healthy
mailpit:
condition: service_started
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: laravel
MYSQL_USER: laravel
MYSQL_PASSWORD: local-password
MYSQL_ROOT_PASSWORD: local-root-password
ports:
- "127.0.0.1:33060:3306"
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-plocal-root-password"]
interval: 5s
timeout: 5s
retries: 10
mailpit:
image: axllent/mailpit:latest
ports:
- "127.0.0.1:8025:8025"
volumes:
mysql-data:
Composeファイルでは、関連するContainerをServiceとして定義します。今回はapp、db、mailpitの3つです。Composeは同じプロジェクトのServiceを共通のNetworkへ接続するため、Service名を使って相互に通信できます。
depends_onのservice_healthyは、MySQLのhealthcheckが成功してからappを起動する指定です。service_startedはMailpitのContainerが起動したことを条件にします。起動したContainerの中のアプリケーションが、すべての処理を終えて利用可能になったことまで保証するものではありません。
MySQLのユーザー名とパスワードは学習用の値です。本番環境では使わず、実際の認証情報をGit管理するComposeファイルへ直接書かないでください。axllent/mailpit:latestは更新されるタグなので、同じImageで再現したい場合は利用時点のバージョンを確認して固定します。
ポートは127.0.0.1にバインドしています。この指定により、ホストPCから確認できる一方、ホストの他のネットワークインターフェースへ公開しない設定になります。Dockerではポートを公開するとホスト外からも到達できる場合があるため、開発用サービスの公開範囲を確認してください。詳しくはDockerのPort publishingに関する説明を参照してください。
Laravelを作成して接続先を設定する
Composeの設定を確認してから、Docker ImageをBuildします。
docker compose config
docker compose build app
Laravel本体は、作成したPHP用ImageのComposerで生成します。
docker compose run --rm --no-deps app composer create-project laravel/laravel .
./src:/var/www/htmlは、Windows側のsrcディレクトリをContainer内の/var/www/htmlへBind Mountする設定です。Container内で生成したLaravelのファイルは、Windows側からも確認できます。
Laravelのsrc/.envで、MySQLの接続先を次のように設定します。
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=local-password
Mailpitへ接続するためのメール設定も行います。
MAIL_MAILER=smtp
MAIL_SCHEME=null
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.test"
MAIL_FROM_NAME="${APP_NAME}"
Containerから別のServiceへ接続するときは、DB_HOST=dbやMAIL_HOST=mailpitのようにComposeのService名を指定します。接続先のIPアドレスはContainerの再作成で変わる可能性がありますが、Service名は同じNetwork内で名前解決できます。
Laravel・MySQL・Mailpitを起動する
まず、MySQLとMailpitを起動します。
docker compose up -d db mailpit
docker compose ps
MySQLの状態がhealthyになったら、LaravelからMigrationを実行します。
docker compose run --rm app php artisan migrate
続けてLaravelを起動します。
docker compose up -d app
ブラウザーで次のURLを開き、Laravelのスタートページを確認します。
http://127.0.0.1:8000
Mailpitの画面はhttp://127.0.0.1:8025で開けます。Laravelからメールを送ってみる場合は、LaravelのTinkerを起動します。
docker compose exec app php artisan tinker
Tinker上でテストメールを送ります。
Illuminate\Support\Facades\Mail::raw(
'Docker + Laravel + Mailpit のテストメールです。',
function ($message) {
$message->to('dev@example.test')
->subject('Mailpit test');
}
);
Mailpitの画面にメールが表示されれば、LaravelからSMTPへの接続を確認できます。Mailpitは開発・テスト用であり、実際の宛先へメールを配信するサービスではありません。既定のSMTPはポート1025で、暗号化や認証を行わないため、本番用メールサーバーとして使わないでください。詳しくはMailpitの設定資料を参照してください。
ホストPCとContainerのポートを区別する
Composeのportsは、ホストPCのポートをContainer内のポートへ割り当てる設定です。たとえば、127.0.0.1:33060:3306はホスト側の33060番ポートをMySQL Containerの3306番ポートへつなぎます。
| 接続元 | 接続先 | 用途 |
|---|---|---|
| Windowsのブラウザー | 127.0.0.1:8000 |
Laravelの画面 |
| WindowsのMySQLクライアント | 127.0.0.1:33060 |
必要に応じてDBを直接確認 |
| Windowsのブラウザー | 127.0.0.1:8025 |
MailpitのWeb UI |
| LaravelのContainer | db:3306 |
MySQLへの接続 |
| LaravelのContainer | mailpit:1025 |
MailpitのSMTPへの接続 |
LaravelとMySQLは同じCompose Networkにいるため、LaravelからMySQLへ接続するだけならMySQLのports設定は必要ありません。ここではWindows上のDBクライアントからも接続できるよう、ホスト側ポートを割り当てています。MailpitのSMTPポート1025も、Container間だけで使うためホストへ公開していません。
Bind MountとNamed Volumeでデータの残り方を比べる
Dockerでは、Container内のファイルをどこに保存するかによって、Containerを削除したときの挙動が変わります。今回の構成には3種類あります。
| データ | 保存方法 | Container削除後 |
|---|---|---|
| Laravelのソース | Bind Mount(./src:/var/www/html) |
Windows側に残る |
| MySQLのデータ | Named Volume(mysql-data) |
Volumeに残る |
| Mailpitの受信メール | Container内のみ | Containerと一緒に消える |
Laravelソースはホスト側のディレクトリをContainerへ見せています。MySQLのデータは、Dockerが管理するNamed Volumeへ保存しています。MailpitにはVolumeを設定していないため、Containerを削除すると受信メールもなくなります。
Containerの削除と再作成を確認するため、次を実行します。
docker compose down
docker compose up -d
docker compose downは、このプロジェクトのContainerとNetworkを削除しますが、Composeファイルに定義したNamed Volumeは通常そのまま残します。再起動後、Migrationの状態を確認します。
docker compose exec app php artisan migrate:status
今回の確認では、Containerを削除して作り直した後も、MySQLのNamed Volumeに保存したMigrationの状態が残っていました。一方、Volumeを設定していないMailpitの受信メールは残りませんでした。Named Volumeも削除するdocker compose down --volumesは、保存データを消す操作なので注意してください。詳細はdocker compose downの公式資料を参照してください。
Container内のlocalhostとService名の違い
LaravelのContainerからMySQLへ接続するときは、DB_HOST=dbを使います。localhostや127.0.0.1は、接続元であるLaravelのContainer自身を指すためです。
今回の環境では、PDOからそれぞれの接続先を指定して挙動を確認しました。まず、Service名dbでは接続できました。
docker compose exec app php -r 'try { new PDO("mysql:host=db;port=3306;dbname=laravel", "laravel", "local-password"); echo "connected", PHP_EOL; } catch (Throwable $e) { echo $e->getMessage(), PHP_EOL; }'
localhostを指定した場合は、次のエラーになりました。
SQLSTATE[HY000] [2002] No such file or directory
この環境のPDO MySQLでは、ホストにlocalhostを指定するとUnix socket経由で接続しようとし、そのsocketがLaravelのContainer内にないため失敗しました。さらに、127.0.0.1を指定した場合はLaravelのContainer自身へTCP接続しようとし、MySQLが動いていないため次のエラーになりました。
SQLSTATE[HY000] [2002] Connection refused
接続先を整理すると、次のようになります。
DB_HOST=db
→ Compose Networkでdb Serviceへ接続
DB_HOST=localhost
→ この環境ではContainer内のUnix socketへ接続しようとして失敗
DB_HOST=127.0.0.1
→ Laravel自身のContainerへTCP接続しようとして失敗
localhostのエラー内容は、クライアントライブラリや接続方法によって異なる場合があります。大事なのは、Container内のlocalhostがWindowsやMySQLのContainerを指すわけではないことです。同じCompose Network上のServiceへ接続するときは、Service名とContainer側のポートを使います。
今回確認できたこと
Laravelの開発環境をDocker Composeで作ると、複数のContainerをひとまとまりの構成として扱えます。それぞれの役割を今回の環境に当てはめると、次のようになります。
- DockerfileからPHP用のImageを作り、そのImageからapp Containerを起動する。
- ComposeのService名を使い、LaravelからMySQLやMailpitへ接続する。
- LaravelのソースはBind Mount、MySQLのデータはNamed Volumeへ保存する。
- ホストPCから画面へアクセスするためのポート公開と、Container同士の通信を分けて考える。
- Containerを削除しても、ホスト側やVolumeへ保存したデータは残る。Container内だけのデータは削除される。
最初から多くの機能を覚えるより、短いDockerfileとcompose.yamlで起動し、データや接続先がどう変わるかを確かめていくと理解しやすいと思います。Containerは作り直せる実行環境として扱い、残したいデータはBind MountやNamed Volumeなど保存先を明示しておきましょう。
参考資料
- Docker Docs: Docker Desktop WSL 2 backend on Windows
- Docker Docs: What is an image?
- Docker Docs: What is a container?
- Docker Docs: Docker Compose Quickstart
- Docker Docs: Networking in Compose
- Docker Docs: Define and manage volumes in Docker Compose
- Docker Docs: docker compose down
- Docker Docs: Port publishing and mapping
- Docker Hub: PHP Official Image
- Mailpit: Configuration
- Mailpit: SMTP server
- Bitbucket Pipelines入門:最小CIの設定から成功・失敗ログの確認まで — CIの実行環境としてDockerを使う例。
- Laravelのcomposer devでMailpit・キューワーカー・Viteをまとめて起動し、キュー化メールを確認する — Dockerを使わないLaravel開発環境でのMailpit利用例。
奈良市を拠点に、27年以上の経験を持つフリーランスWebエンジニア、阿部辰也です。
これまで、ECサイトのバックエンド開発や業務効率化システム、公共施設の予約システムなど、多彩なプロジェクトを手がけ、企業様や制作会社様のパートナーとして信頼を築いてまいりました。
【制作会社・企業様向けサポート】
Webシステムの開発やサイト改善でお困りの際は、どうぞお気軽にご相談ください。小さな疑問から大規模プロジェクトまで、最適なご提案を心を込めてさせていただきます。
ぜひ、プロフィールやWeb制作会社様向け業務案内、一般企業様向け業務案内もご覧くださいね。
Laravelのコードレビューで遅いSQLの候補を洗い出す
2026.10.02
Laravelのコードレビューで、ループ内のDBアクセスやEloquentのN+1、大量取得、検索条件とインデックスなど、性能問題につながりやすい箇所を候補として洗い出します。Laravelで発行SQLと実行時間を確認し、MySQLのEXPLAINや実データに近い環境での計測へつなげる方法を紹介します。コードレビューだけではSQLの実行時間を断定できない点も説明します。
LaravelのArtisanコマンドを本番運用する:冪等性・二重実行防止・再実行の設計
2026.09.22
LaravelのArtisanコマンドでデータベースを更新する処理を、本番環境で安全に運用するための考え方を整理します。商品在庫の一括更新を例に、冪等性、dry-run、チャンク処理、トランザクション、二重実行防止、失敗後の再実行、ログや終了コード、実行前後の確認手順を紹介します。
Laravel 13のNamed Rate Limiterでレート制限を実装する:IP・ユーザー・入力値ごとの設定
2026.09.19
LaravelのNamed Rate Limiterを使い、無制限に繰り返されたくない処理へレート制限を追加する方法を解説します。RateLimiter::for()とthrottleミドルウェアの基本から、IPアドレス・ユーザーID・入力値による制限、複数の制限、429レスポンス、ログイン処理への応用、Redis利用時の注意点まで整理します。
Laravel SocialiteでGoogleログインを実装する|OAuth設定から初回登録まで
2026.09.18
Laravel Socialiteを使ったGoogleログインを、OAuthクライアントの設定からコールバック、Laravelユーザーの登録・ログインまで解説します。Googleアカウントは変更されるメールアドレスではなくsubで識別し、初回登録時は検証済みメールアドレスを確認します。既存アカウントへ自動連携しない設計や、セッション管理、Socialite Fakeで確認するテスト項目も紹介します。