NginxとPHP-FPMをDocker Composeで組み合わせると、本番に近い構成をローカルですばやく再現できます。Nginxが静的配信とリバースプロキシ、PHP-FPMがPHPの実行を担い、ソースはボリュームで共有します。ここでは最小構成から拡張のヒントまで、実務でそのまま使える形で手順をまとめます。
ディレクトリ構成を用意する
プロジェクト直下にCompose、Nginx設定、PHP用Dockerfileと設定、アプリの公開ディレクトリを並べます。public配下にindex.phpを置き、Nginxのドキュメントルートにします。
project/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
├─ php/
│ ├─ Dockerfile
│ └─ php.ini
└─ public/
└─ index.php
docker-compose.ymlを書く
NginxとPHP-FPMを同一ネットワークで接続し、ソースコードを両コンテナにマウントします。Nginxはホストのポートを公開し、PHP-FPMは内部で待ち受けるだけにします。
version: "3.9"
services:
nginx:
image: nginx:1.27-alpine
container_name: app_nginx
ports:
- "8080:80"
volumes:
- ./public:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1/healthz || exit 1"]
interval: 10s
timeout: 3s
retries: 5
php:
build:
context: ./php
container_name: app_php
environment:
PHP_IDE_CONFIG: "serverName=docker"
TZ: "Asia/Tokyo"
volumes:
- ./public:/var/www/html
healthcheck:
test: ["CMD", "php", "-v"]
interval: 30s
timeout: 5s
retries: 3
networks:
default:
name: app_net
Nginxの仮想ホスト設定を追加する
ドキュメントルートを/var/www/htmlにし、PHPはphpサービスの9000番へfastcgiで渡します。フロントコントローラ方式のフレームワークでも動くようtry_filesでindex.phpにフォールバックします。
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php;
location /healthz {
access_log off;
return 200 "ok";
add_header Content-Type text/plain;
}
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 php:9000;
fastcgi_read_timeout 60s;
}
location ~* \.(png|jpg|jpeg|gif|webp|svg|css|js|ico|woff2?)$ {
expires 7d;
access_log off;
}
client_max_body_size 20m;
sendfile on;
}
PHP-FPMのDockerfileを用意する
公式のphp:fpmイメージをベースに、よく使う拡張をインストールして設定をコピーします。必要に応じてpdo_mysqlなどを追加します。
# php/Dockerfile
FROM php:8.2-fpm-alpine
RUN apk add --no-cache icu-dev libpng-dev libjpeg-turbo-dev oniguruma-dev \
&& docker-php-ext-configure intl \
&& docker-php-ext-install -j$(nproc) intl opcache pdo pdo_mysql
COPY php.ini /usr/local/etc/php/conf.d/zzz-custom.ini
WORKDIR /var/www/html
USER www-data
PHPの設定を最小限整える
開発中はエラー表示を有効化し、タイムゾーンやOPcacheの基本設定を入れておきます。本番ではdisplay_errorsをオフに切り替えます。
; php/php.ini
date.timezone = Asia/Tokyo
memory_limit = 256M
upload_max_filesize = 20M
post_max_size = 21M
display_errors = On
error_reporting = E_ALL
opcache.enable=1
opcache.enable_cli=0
opcache.validate_timestamps=1
opcache.revalidate_freq=2
動作確認用のindex.phpを置く
PHPが実行されること、サーバー変数が正しく渡っていることを確認します。不要になったらアプリ本体に差し替えます。
<?php
header('Content-Type: text/plain; charset=utf-8');
echo "Hello from PHP-FPM on Docker\n";
echo "PHP: " . PHP_VERSION . "\n";
echo "Server: " . ($_SERVER['SERVER_SOFTWARE'] ?? 'n/a') . "\n";
起動と確認を行う
コンテナをビルドして立ち上げ、ブラウザでhttp://localhost:8080 にアクセスします。ログにエラーが出ていないかも確認します。
docker compose build
docker compose up -d
docker compose logs -f nginx
docker compose logs -f php
権限とパフォーマンスの勘所を押さえる
マウントしたソースの所有者はwww-dataで実行されます。開発環境では大半の操作が問題なく行えますが、キャッシュやアップロード先をpublic外の書き込み可能ディレクトリに分けると安全です。静的アセットはNginxが直接配信し、PHPは動的処理に集中させます。OPcacheはvalidate_timestampsで即時反映を優先し、本番ではrevalidate_freqを延ばして効率を上げます。
XdebugやSSLを加えるときの指針
ステップデバッグが必要になったらphp-fpmイメージにpeclでxdebugを追加し、remote_hostをhost.docker.internalへ向けます。ローカルでHTTPSが欲しいときはmkcertやtraefik/caddyをフロントに置く構成に切り替え、Nginxは上流リバースプロキシの背後で80番待ち受けとします。
よくあるトラブルへの対処
502 Bad Gatewayが出る場合はphpコンテナの起動状態とfastcgi_passのホスト名を見直します。ダウンロードダイアログになる場合はNginxのlocation ~ \.php のブロックが一致していない可能性があります。権限エラーはマウント先の書き込み権を確認し、必要に応じてキャッシュディレクトリのみパーミッションを緩めます。ポート競合が出たらComposeの公開ポートを変更します。
まとめと次の一歩
Nginxをフロント、PHP-FPMをアプリ実行に分離した二層構成は、本番を意識したローカル開発の定番です。Composeでサービスを定義し、Nginxのfastcgi設定とPHP-FPMの拡張・ini調整を最小限に整えれば、すぐに動く土台が完成します。この土台にデータベースやキャッシュ、キューなどを追加し、環境変数とボリュームの整理を進めると、より実践的なコンテナ開発環境が構築できます。