DockerでLaravel開発環境を構築する方法|SailとカスタムComposeを比較検証

Laravel入門

LaravelでWebアプリケーションを開発する際、環境構築のデファクトスタンダードとなっているのがDockerです。ホストPCの環境を汚さず、チーム全員で全く同一のPHPバージョンやデータベース構成を再現できるため、実務開発において必須のスキルといえます。

しかし、「DockerでLaravel環境を作る」と一口に言っても、Laravel公式の「Laravel Sail」を使う方法と、自前で「Dockerfileやcompose.yaml」を記述するカスタム構成の2つの選択肢があり、「どちらを選ぶべきか」「何が違うのか」で迷う方は少なくありません。

本記事では、Laravel 11 / 12 / 13およびPHP 8.3 / 8.4に対応した環境をもとに、「Laravel Sail」と「カスタムDocker Compose」の違いの徹底比較から、それぞれの具体的な構築手順、Vite連携、現場で頻発するエラーと解決策まで、実機検証済みのコードとともに徹底解説します。

Sail vs カスタムDocker Compose|違いと選び方【比較表】

まずは、Laravel Sailと自前Docker Composeの主要な違いを整理しましょう。

比較項目 Laravel Sail(公式ツール) カスタムDocker Compose(自前構築)
概要 Laravel公式が提供するDocker開発環境CLIラッパー Dockerfileやcompose.yamlを自分で記述する構成
構築スピード ◎ コマンド1発(数分で起動完了) △ 各種設定ファイル(Nginx/PHP/DB)の記述が必要
Dockerの知識 ○ ほぼ不要(コンテナを意識せず使える) △ Dockerfileやネットワーク、権限の知識が必要
Webサーバー構成 PHP組み込みサーバー / 単一コンテナ内包型 Nginx + PHP-FPM 分離型(本番構成に準拠可能)
日常のコマンド操作 sail artisan ... など直感的で快適 docker compose exec app php artisan ...
カスタマイズ性 △ Sailの既定構造に縛られる(publishで拡張可) ◎ 拡張モジュールやミドルウェアを完全自由に設計可能
本番環境への流用 × ローカル開発専用設計 ◎ Dockerfileや設定を本番コンテナへ転用しやすい
おすすめの対象者 ・Laravel初心者、個人開発者
・手軽に素早く開発を始めたい方
・実務のチーム開発や本番環境を意識したい方
・NginxやPHP拡張を細かく制御したい方

どちらを選ぶべきかの判断基準

  • 「まずは手軽にLaravelを動かしたい」「Dockerの細かい設定で消耗したくない」場合:迷わずLaravel Sailを選びましょう。公式サポートされており、MySQL、Redis、Mailpitなどの連携もコマンド1つで完結します。
  • 「NginxとPHP-FPMを分けた本番に近い構成を学びたい」「特定のPHP拡張やライブラリを追加したい」場合カスタムDocker Composeが適しています。サーバーアーキテクチャの理解が深まり、AWS ECSやGCP Cloud Run、Kubernetesなどのコンテナ本番デプロイにも応用が効きます。

動作検証環境

本記事で紹介するコードと手順は、以下の最新環境で動作確認を行っています。

  • Laravel: 13.x / 12.x / 11.x
  • PHP: 8.3 / 8.4
  • Docker Engine: v27.x 以降 / Docker Desktop 4.x
  • Docker Compose: v2.x(compose.yaml形式)
  • Database: MySQL 8.4 / 8.0

【注意】Docker Compose v2とcompose.yamlについて
従来のdocker-compose(ハイフン付きのv1)はサポートが終了しており、現在はプラグイン型のdocker compose(スペース区切り・v2)が標準です。また、設定ファイル名もdocker-compose.ymlからcompose.yamlが推奨規格となっています(Laravel Sailも最新版ではcompose.yamlを生成します)。

方法1:Laravel Sailで最速セットアップ(公式推奨)

Laravel Sailは、ホストマシンにPHPやComposer、Node.jsを一切インストールしていなくても、Dockerさえあれば一瞬でフルスタック環境を立ち上げられる公式開発環境です。

新規プロジェクトを作成する場合

ターミナルで以下のcurlコマンドを実行するだけで、Sailが組み込まれた新規Laravelプロジェクトが作成されます。

# MySQL、Redis、Mailpitを含んだ「example-app」を作成
curl -s "https://laravel.build/example-app?with=mysql,redis,mailpit" | bash

# プロジェクトディレクトリに移動
cd example-app

# バックグラウンドでコンテナ起動
./vendor/bin/sail up -d

起動後、ブラウザで http://localhost にアクセスすると、Laravelの初期画面が表示されます。

既存のLaravelプロジェクトにSailを追加する場合

すでに存在するプロジェクトにSailを導入する場合は、Composerでパッケージを追加してインストールコマンドを実行します。

# 1. Sailを開発依存としてインストール
composer require laravel/sail --dev

# 2. compose.yamlと設定を生成
php artisan sail:install --with=mysql,redis,mailpit

# 3. コンテナを起動
./vendor/bin/sail up -d

# 4. マイグレーションを実行
./vendor/bin/sail artisan migrate

詳しいインストールオプションや対話形式での設定については、sail:install — Sailをインストールするコマンドで詳しく解説しています。

Sailで生成されるcompose.yamlの仕組み

sail:installを実行すると、プロジェクト直下にcompose.yamlが生成されます。その主要な構造は以下のようになっています。

services:
    laravel.test:
        build:
            context: './vendor/laravel/sail/runtimes/8.4'
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: 'sail-8.4/app'
        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
            - redis
    mysql:
        image: 'mysql:8.4'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
        volumes:
            - 'sail-mysql:/var/lib/mysql'
        networks:
            - sail
    redis:
        image: 'redis:alpine'
        volumes:
            - 'sail-redis:/data'
        networks:
            - sail
networks:
    sail:
        driver: bridge
volumes:
    sail-mysql:
        driver: local
    sail-redis:
        driver: local

Sailの特徴は、laravel.testコンテナの中にPHP CLI、組み込みWebサーバー、Node.js、Composer、Supervisordなどがすべてパッケージ化されている点です。これにより、Webサーバー用コンテナを別途立てる必要がなく、シンプルな1コンテナ構成で動作します。

よく使うSailコマンドとエイリアス設定

毎回./vendor/bin/sailと入力するのは手間がかかるため、シェルの設定ファイル(~/.bashrc~/.zshrc)にエイリアスを登録しておきましょう。

alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'

設定を反映(source ~/.zshrc)させると、以降はsailコマンドが使えます。

# コンテナの起動・停止
sail up -d
sail down

# Artisanコマンドの実行
sail artisan migrate
sail artisan make:model Post -m

# Composer / NPM の実行
sail composer require laravel/breeze --dev
sail npm install
sail npm run dev

# データベースCLIへのログイン
sail mysql

# コンテナ内シェルへ入る
sail shell

Sailのさらに詳しいコマンドや活用術については、Laravel Sailとは?初心者向け導入・起動停止・よく使うコマンド・Dockerとの関係を徹底解説をご覧ください。

方法2:カスタムDocker Composeで構築する(LEMP構成)

実務開発や本番環境へのデプロイを想定する場合、Webサーバー(Nginx)、アプリケーションサーバー(PHP-FPM)、データベース(MySQL)を独立したコンテナとして分離するLEMPスタック構成が標準的です。

ディレクトリ構成

プロジェクトルートにdockerディレクトリを作成し、各設定ファイルを整理して配置します。

laravel-docker-app/
├── docker/
│   ├── nginx/
│   │   └── default.conf       # Nginxのバーチャルホスト設定
│   └── php/
│       ├── Dockerfile         # PHP-FPM用ビルド定義
│       └── php.ini            # PHPの動作設定
├── compose.yaml               # Docker Compose構成ファイル
├── .env                       # 環境変数設定
├── app/
├── bootstrap/
├── config/
├── public/
├── routes/
├── storage/
└── composer.json

1. Dockerfileの作成(docker/php/Dockerfile)

PHP 8.3 / 8.4のFPM Alpineイメージをベースに、Laravelの動作に必要な拡張モジュール(PDO MySQL、BCMath、GD、Zip、OPcache等)をインストールし、Composerをマルチステージビルドで組み込みます。

FROM php:8.3-fpm-alpine

# システム依存パッケージのインストール
RUN apk add --no-cache \
    git \
    curl \
    zip \
    unzip \
    libzip-dev \
    libpng-dev \
    libjpeg-turbo-dev \
    freetype-dev \
    oniguruma-dev \
    icu-dev \
    linux-headers

# PHP拡張モジュールのインストール
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) \
        pdo_mysql \
        mbstring \
        zip \
        bcmath \
        gd \
        intl \
        opcache

# Composerの公式イメージからバイナリをコピー
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# 作業ディレクトリの設定
WORKDIR /var/www

# コンテナ起動コマンド
CMD ["php-fpm"]

2. PHP設定ファイルの作成(docker/php/php.ini)

ローカル開発に適したメモリ上限やファイルアップロードサイズを設定します。

[PHP]
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 60
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
default_charset = "UTF-8"

[Date]
date.timezone = "Asia/Tokyo"

[mbstring]
mbstring.language = "Japanese"

3. Nginx設定ファイルの作成(docker/nginx/default.conf)

NginxからPHP-FPMコンテナ(app:9000)へリクエストを転送し、Laravelのpublic/index.phpへルーティングします。

server {
    listen 80;
    server_name localhost;
    root /var/www/public;
    index index.php index.html;

    charset utf-8;

    # すべてのリクエストをLaravelのフロントコントローラーへ渡す
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # ファビコンとrobots.txtのログを抑制
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    # エラーページ設定
    error_page 404 /index.php;

    # PHP-FPMへのプロキシ設定
    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
    }

    # .envや.gitなどの隠しファイルへのアクセスを拒否
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Nginxの設定についてより深く知りたい方は、LaravelとNginxの設定方法:初心者でも簡単に環境構築する方法も参考にしてください。

4. compose.yamlの作成

app(PHP-FPM)、web(Nginx)、db(MySQL)の3つのコンテナを連携させる定義をプロジェクトルートに記述します。

services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./docker/php/php.ini:/usr/local/etc/php/conf.d/custom.ini
    networks:
      - laravel-net
    depends_on:
      - db

  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - laravel-net
    depends_on:
      - app

  db:
    image: mysql:8.4
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: ${DB_DATABASE:-laravel}
      MYSQL_USER: ${DB_USERNAME:-laravel}
      MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-secret}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - laravel-net

networks:
  laravel-net:
    driver: bridge

volumes:
  db_data:
    driver: local

5. .envファイルのデータベース設定

コンテナ間の通信では、データベースのホスト名(DB_HOST)に127.0.0.1localhostではなく、Docker Composeのサービス名である「db」を指定します。

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret

データベース接続の基本や設定項目については、LaravelとMySQLの接続設定ガイドで詳しく解説しています。

6. 起動と初期セットアップ手順

設定ファイルが揃ったら、コンテナをビルド・起動し、アプリケーションの初期化を行います。

# 1. コンテナのビルドと起動
docker compose up -d --build

# 2. Composerパッケージのインストール
docker compose exec app composer install

# 3. アプリケーションキーの生成
docker compose exec app php artisan key:generate

# 4. ディレクトリ権限の修正(書き込みエラー対策)
docker compose exec app chmod -R 775 storage bootstrap/cache
docker compose exec app chown -R www-data:www-data storage bootstrap/cache

# 5. マイグレーションの実行
docker compose exec app php artisan migrate

ブラウザで http://localhost:8080 にアクセスし、Laravelの初期画面が表示されれば環境構築完了です。

Docker環境でのVite(フロントエンド開発)設定

LaravelのフロントエンドビルドツールであるViteをDocker上で動かす場合、ホットモジュールリプレイスメント(HMR)用のポート5173を開放し、Vite設定を調整する必要があります。

1. compose.yamlにポート5173を追加

compose.yamlappサービスにポートフォワーディングを追加します。

  app:
    # ... 他の設定 ...
    ports:
      - "5173:5173"

2. vite.config.jsの設定修正

ホストマシンのブラウザからコンテナ内のViteサーバーへWebSocket接続できるよう、serverオプションを追記します。

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
    server: {
        host: '0.0.0.0', // コンテナ外からのアクセスを許可
        port: 5173,
        hmr: {
            host: 'localhost', // ブラウザからのHMR接続先
        },
    },
});

これで、docker compose exec app npm run dev(Sailならsail npm run dev)を実行すると、BladeファイルやCSS/JSの変更がブラウザに即座にホットリロードされます。

Docker + Laravelで初心者がつまずくエラーと解決策

エラー現象・メッセージ 原因 具体的な解決策
Permission denied / 500エラー
The stream or file "...laravel.log" could not be opened: failed to open stream: Permission denied
ホストとコンテナ(www-data)のユーザーUIDの不一致により、storage/bootstrap/cache/への書き込みが拒否されている docker compose exec app chmod -R 775 storage bootstrap/cache および docker compose exec app chown -R www-data:www-data storage bootstrap/cache を実行する
データベース接続エラー
SQLSTATE[HY000] [2002] Connection refused
.envDB_HOST127.0.0.1localhostのままになっている(コンテナ自身を指してしまう) .envDB_HOSTをDockerサービス名(カスタムならdb、Sailならmysql)に変更する
ポート競合エラー
Bind for 0.0.0.0:80 failed: port is already allocated
ホストマシン上の既存Webサーバー(Apache/Nginx)や別のコンテナがポート80/3306を使用中 compose.yamlのホスト側ポートを8080:8033060:3306に変更する(Sailなら.envAPP_PORT=8080を指定)
Windows (WSL2) で動作が異常に重い プロジェクトファイルがWindows側のCドライブ(/mnt/c/...)に置かれている WSL2内のLinuxネイティブ領域(/home/ユーザー名/...)にプロジェクトを作成・移動する
コンテナ起動時にマイグレーションが失敗する
SQLSTATE[HY000] [2002] php_network_getaddresses
MySQLコンテナの初期化完了前にLaravelのマイグレーションが走ってしまった MySQLのヘルスチェック(healthcheck)を設定するか、MySQLコンテナが完全起動するまで数秒待ってからmigrateを実行する

まとめと関連記事

Dockerを使ったLaravel環境構築は、用途とスキルレベルに応じて最適な手段を選ぶことが成功の鍵です。

  • 最速で開発を始めたい・Dockerに詳しくないLaravel Sailcurl -s "https://laravel.build/app" | bash
  • 本番環境に近いマルチコンテナ構成を学びたい・自由にカスタムしたいカスタムDocker Compose(Nginx + PHP-FPM + MySQL)
  • 権限エラー(Permission denied)が出たらstoragebootstrap/cacheの権限(775 / www-data)を修正
  • DB接続に失敗したらDB_HOSTがコンテナサービス名(db / mysql)になっているか確認

どちらの方法を選んでも、Dockerを活用することで環境差異によるトラブルをなくし、再現性の高い快適な開発環境を手に入れることができます。

あわせて読みたい関連記事

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント