Laravel×Dockerで開発環境を最短で安定させる:ローカル統一・再現性・速度を両立する構成

Laravel×Dockerで開発環境を最短で安定させる:ローカル統一・再現性・速度を両立する構成

Laravel開発をDocker化すると、PHP/Node/DB/Redisなどの依存関係をチームで統一でき、OS差異や「動く人と動かない人」を減らせる。重要なのは、ただコンテナを立てることではなく、コード編集・依存インストール・DBマイグレーション・キュー/スケジューラ・テスト・本番相当設定までを“毎回同じ手順”で回せる状態にすること。ここではLaravelとDocker Composeで、開発速度と再現性を両立する構成を組み立てる。

ゴール設定:Docker化で解決したい問題を先に固定する

典型的な狙い:
・PHP/Composer/Nodeのバージョン差をゼロにする
・MySQL/PostgreSQL/Redisなどをワンコマンドで起動
・新メンバーが「clone→起動→動作確認」まで最短
・CIとローカルを同じ構成に寄せる
・本番との差分(拡張、ini、タイムゾーン、ロケール)を見える化する
ゴールが曖昧だと、Dockerは「遅い」「難しい」に寄りやすい。

構成の基本:app / web / db / cache / queue を分けて考える

開発でよく使う分割:
・app:PHP-FPM(Laravel本体)
・web:Nginx(またはCaddy)
・db:MySQL/PostgreSQL
・cache:Redis
・queue:queue worker(appと同イメージで別サービス)
・scheduler:cron(Laravel schedule:run を回す)
最小は app+db でも良いが、後で増やす前提で設計すると崩れにくい。

ディレクトリ設計:docker配下に設定を集約する

おすすめ例:
・docker/nginx/default.conf
・docker/php/Dockerfile
・docker/php/php.ini
・docker/mysql/initdb.d/
・docker/postgres/initdb.d/
Docker関連が散らばると、メンテが破綻しやすい。

docker-compose.yml:全体のサービス定義(最小~実用)

LaravelはPHP-FPM+Nginxが定番。DBとRedis、必要ならMailhogも足す。

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: laravel_app
    working_dir: /var/www
    volumes:
      - ./:/var/www
    environment:
      TZ: Asia/Tokyo
    depends_on:
      - db
      - redis

  web:
    image: nginx:1.27-alpine
    container_name: laravel_web
    ports:
      - "8080:80"
    volumes:
      - ./:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  db:
    image: mysql:8.4
    container_name: laravel_db
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: root
      TZ: Asia/Tokyo
    volumes:
      - dbdata:/var/lib/mysql

  redis:
    image: redis:7-alpine
    container_name: laravel_redis
    ports:
      - "6379:6379"

volumes:
  dbdata:

ポイント:
・ソースコードは volumes でマウント(編集→即反映)
・DBは named volume で永続化
・ポートは開発用に固定(8080など)
・TZを揃えてログ・日時ズレを防ぐ

PHP Dockerfile:拡張・Composer・ユーザー権限を最初に固める

Laravelでよく要る拡張:pdo_mysql、mbstring、intl、zip、gd、bcmath、opcache など。Composerを入れて、ホストとの権限問題も避ける。

# docker/php/Dockerfile
FROM php:8.3-fpm

ENV TZ=Asia/Tokyo

RUN apt-get update && apt-get install -y \
    git unzip libzip-dev libicu-dev libpng-dev libjpeg-dev libfreetype6-dev \
  && docker-php-ext-configure gd --with-freetype --with-jpeg \
  && docker-php-ext-install pdo_mysql mbstring intl zip bcmath gd opcache \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www

発生しやすい問題:
・拡張不足で Class "PDO" not found / ext-xxx is missing
・GD/Intlの依存ライブラリ不足
・権限問題で storage / bootstrap/cache に書けない

Nginx設定:public配下・index.phpへのフォールバックを正しく

Laravelは全リクエストをpublic/index.phpに流す。これが崩れると404祭りになる。

# docker/nginx/default.conf
server {
  listen 80;
  server_name _;
  root /var/www/public;

  index index.php;

  location / {
    try_files $uri $uri/ /index.php?$query_string;
  }

  location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass app:9000;
  }

  location ~* \.(css|js|png|jpg|jpeg|gif|svg|ico)$ {
    expires 7d;
    access_log off;
  }
}

.envの整備:Docker向けにDBホストをサービス名にする

Docker内では 127.0.0.1 ではなく、Composeのサービス名(db/redis)で接続する。

# .env(例)
APP_URL=http://localhost:8080

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

CACHE_STORE=redis
REDIS_HOST=redis
REDIS_PORT=6379

エラー発生条件:
・DB_HOSTがlocalhostのままで接続失敗
・APP_URLがズレて、URL生成やCSRFが崩れる
・キャッシュ/セッションドライバだけ本番寄りで、Redis未起動

初回セットアップ:ワンコマンドで起動→依存→キー生成→マイグレーション

手順を固定すると、チームが詰まらない。

# 起動
docker compose up -d --build

# Composerインストール
docker compose exec app composer install

# APP_KEY
docker compose exec app php artisan key:generate

# マイグレーション
docker compose exec app php artisan migrate

ホットリロード:ViteをDockerで動かしてフロント開発を速くする

フロントもDockerに載せると統一できるが、ホスト実行の方が速い場合もある。Dockerでやるならポート公開とhost指定が必要。

# 例:appコンテナ内で
docker compose exec app npm install
docker compose exec app npm run dev -- --host 0.0.0.0 --port 5173

運用ポイント:
・Viteのポート(5173)をcomposeで公開する
・WS接続が必要なのでhost設定が重要
・Windows環境はファイル監視が遅いことがある(設定で改善)

QueueとScheduler:開発でも“動く前提”で回しておく

キューやスケジューラをローカルで回さないと、本番だけ壊れる。別サービスで立てると管理が楽。

# キューワーカー(手動実行でもOK)
docker compose exec app php artisan queue:work

# スケジューラ(開発ならrunでも可)
docker compose exec app php artisan schedule:run

本格運用なら compose に queue / scheduler サービスを追加し、常時起動に寄せる。

ストレージ権限:storage と bootstrap/cache を確実に書ける状態にする

Laravelが書き込めないと、ログもキャッシュもセッションも壊れる。ホストOSとUID/GIDがズレると頻発する。

# まずは開発用に権限を整える例
docker compose exec app bash -lc "chmod -R 775 storage bootstrap/cache"

より堅くするなら、Dockerfileでユーザー作成+権限設計まで固定する。

DB永続化とリセット:named volume運用と初期化手順

DBを消す操作は事故りやすいので、手順を明文化する。

docker compose down -v

# DBは残してアプリだけ再起動
docker compose down
docker compose up -d

テストの回し方:同じコンテナでユニット/Featureを走らせる

テストもDockerで統一すると、CIとの差が減る。

# PHPUnit / Pest(どちらでも)
docker compose exec app php artisan test

DBを分けたい場合は test 用DBを別に用意し、.env.testing をDocker前提で作る。

速度改善:遅いDockerを“開発速度が出るDocker”に寄せる

効くポイント:
・依存(vendor/node_modules)をボリューム分離(OSによって体感が変わる)
・Composer/NPMのキャッシュを使う
・不要なサービスを起動しない(profiles)
・イメージのレイヤを整理してビルドを速くする
・Windows/MacのファイルI/O問題は、マウント方式やWSL2設定で大きく改善することがある

よくあるエラーと即チェック項目

・Cannot Connect to the Docker Daemon:Docker Desktop起動、権限、service状態
・Connection refused (DB/Redis):サービス名接続、depends_on、ポート、初期化待ち
・Permission denied (storage):UID/GID、chmod/chown、Dockerfileのユーザー
・404/502(Nginx):rootがpublicか、fastcgi_passがapp:9000か、try_files
・APP_KEY missing:key:generate未実行、.envの読み込み不備

まとめ:Laravel×Docker開発環境を“使える形”で固定する要点

・サービスを役割で分け、Composeで一括起動に寄せる
・DB/Redisの接続先はサービス名に統一
・DockerfileでPHP拡張とComposerを固定し、再現性を担保
・Nginxのpublic/try_filesを正しく設定
・キュー/スケジューラ/テストもローカルで回る前提にする
・速度と権限問題は早めに潰し、チーム手順をコマンドで統一する