PHP-FPMを「なんとなく速くなる仕組み」としか捉えていないと、本番で502や504が出た瞬間に、どこから手を付けるべきか分からず時間と信用を失います。実務で結果を分けるのは、php-fpm とは何かという用語ではなく、NginxやApacheとの連携、pm.max_childrenやpm.start_serversの設計、ログとstatusページによる切り分けを、一連のロジックとして運用できるかどうかです。一般的な解説は「FastCGIプロセスマネージャの概要」「インストールと再起動手順」「www.confの主な設定」「ログの場所」といった点で止まりがちですが、それだけではピーク時の落ち方も、DockerやWindows、共有ホスティングでの挙動差も読み切れません。この記事では、UbuntuやCentOSでのphp-fpm インストールと再起動、バージョン確認、NginxやApacheとのphp-fpm 設定、WordPressやLaravelの構成例までをつなぎ、1プロセスあたりのメモリから逆算するチューニングと、ログ・slowlog・statusページを使ったトラブルシュートを軸に整理します。読み終えるころには、「php-fpm が起動しない」「プロセスが残る」「Dockerでは動くのに本番で落ちる」といった事象を、感覚ではなく設計と検証の手順でつぶせる状態になっているはずです。
目次
PHP-FPMとは何かを3分でつかむ仕組みと「変わるもの・変わらないもの」
アプリは同じPHPなのに、構成を変えただけで急に軽くなったり、逆に502まみれになったりします。そこで効いてくるのが、このプロセスマネージャです。ここを腹落ちさせておくと、NginxでもApacheでも本番トラブルに強い構成を選べるようになります。
PHP-FPMとはFastCGIプロセスマネージャとして何をしているのかをやさしく解説
ざっくり言うと、次の2つだけを担当する仕組みです。
-
PHPを実行するワーカーを、あらかじめ必要な数だけ立ち上げておく
-
ワーカーの数や寿命を監視し、増減・再起動を自動で行う
Webサーバ(NginxやApache)からくるFastCGIリクエストを受け取り、空いているPHPプロセスに振り分ける“受付係”兼“シフト管理者”だと考えるとイメージしやすいです。
変わるものと変わらないものを切り分けると、設計の迷いが減ります。
| 項目 | 変わるもの | 変わらないもの |
|---|---|---|
| Webサーバ | Nginx / Apache / Caddyなどで変更可能 | PHPコードそのもの |
| PHP実行方式 | CGI / mod_php / FastCGIプロセスマネージャ | PHPの文法・フレームワーク |
| チューニング対象 | pm設定、ソケット、バックログ | ビジネスロジック |
アプリのバグはこの仕組みに変えても消えませんが、「さばける同時接続数」「落ちるかどうか」はここで大きく変わります。
CGIやFastCGIやmod_phpとPHP-FPMの違いを実運用でズバッと比較
現場でよく混ざる4つを、パフォーマンスと運用の観点で整理します。
| 方式 | 仕組み | 強み | 弱み・ハマりどころ |
|---|---|---|---|
| CGI | リクエストごとにPHPプロセスをfork | 実装が単純、隔離しやすい | プロセス起動コストが重く高負荷に弱い |
| FastCGI | 常駐プロセスにリクエストを投げる | 低オーバーヘッド | プロセス管理を自分で実装する必要 |
| mod_php | Apacheの子プロセスにPHPを組み込み | 設定が簡単、古い環境で多い | Apacheプロセスごとにメモリを消費、スケールしづらい |
| FastCGIプロセスマネージャ | FastCGIのプロセス管理専任 | pm設定で柔軟にスケール、Nginxと相性良い | 設計をミスるとメモリ食い尽くしやすい |
私の視点で言いますと、本番で事故りやすいのは「mod_phpからFastCGIプロセスマネージャに移行したのに、1プロセスあたりのメモリを見積もらずにpm.max_childrenだけ増やしたケース」です。CPUは余っているのに、メモリとswapで応答がガタ落ちする典型パターンです。
なぜNginxやApacheはPHP-FPMと一緒に使われ続けているのか
選ばれ続けている理由は、主に次の3点です。
-
役割分担がはっきりしている
NginxやApacheは「HTTPと静的ファイルの配達」、FastCGIプロセスマネージャは「PHP実行とプロセス管理」と役割が分かれるため、どちらか一方のチューニングに集中しやすくなります。
-
スケール戦略を柔軟にとれる
pm.start_serversやpm.max_childrenを変えるだけで、「低メモリで細く長く」「メモリ潤沢で太く短く」など、サーバスペックに合わせた運用が可能です。DockerやKubernetesでもコンテナ1つあたりの並列数をコントロールしやすくなります。
-
トラブルシュートの切り分けが明確
502や504が出たとき、Nginxのerror_logとFastCGIプロセスマネージャのerror_log・slowlogを見比べれば、「ネットワークやタイムアウトの問題」か「PHPプロセス側の枯渇・クラッシュ」かを秒単位で判断できます。statusページを有効化しておけば、リクエスト数・キュー長・アクティブプロセス数も一目で追えます。
このように、単なる「速くなる魔法のモジュール」ではなく、Webサーバとアプリケーションの間に“観測可能なレイヤ”を挟めることが、本番運用で選ばれ続けている最大の理由です。
NginxとApacheでPHP-FPMをどうつなぐか構成パターンや落とし穴を完全攻略
「とりあえず動く」設定から一歩抜け出すと、NginxやApacheとfpmのつなぎ方だけで、安定性もレスポンスもガラッと変わります。ここでは本番トラブルを何度も踏んできた現場目線で、構成パターンと落とし穴を整理します。
NginxとPHP-FPMの設定でfastcgi_passやUNIXソケットと9000番ポートを賢く選ぶコツ
Nginx側で肝になるのは、locationとfastcgi_pass、そしてlisten先をUNIXソケットにするかTCPポート(:9000など)にするかです。
まず、よく使う構成パターンを整理します。
| 項目 | UNIXソケット (/run/php-fpm.sock など) | TCPポート (:9000 など) |
|---|---|---|
| 性能 | オーバーヘッドが小さく低レイテンシ | 若干遅いが差は小さい |
| 可観測性 | netstatなどで見えにくい | ss/netstatで接続数が見やすい |
| 分散構成 | 同一ホスト前提 | 別サーバーのfpmにも接続しやすい |
| トラブル時の切り分け | 権限ミスが多い | ファイアウォール・接続数上限が絡みやすい |
同一サーバーでNginxとfpmを動かすなら、ソケット優先で検討しつつ、権限設計を最初に固めるのが現場の定番です。/var/run や /run にsockファイルを置くときは、プール設定のuser・groupとNginxワーカーのユーザーを揃えないと「502が出たり出なかったり」といういやらしい症状になります。
Nginx側locationでは、次の観点を外さないことが重要です。
-
indexディレクティブでindex.phpを必ず含める
-
fastcgi_param SCRIPT_FILENAMEで、ドキュメントrootとリクエストURIを正しく連結する
-
try_filesで静的ファイルとphpスクリプトの振り分けを明示する
ブラックボックスに見えがちなfastcgi_passも、「Nginxはhttpを受けてFastCGIリクエストに変換してfpmへ投げているだけ」と意識すると、locationの組み立てが一気にクリアになります。
ApacheとPHP-FPMの連携運用mod_php卒業による「軽さ」と「役割分担」が見えてくる
Apacheからfpmへ切り替えるとき、mod_phpの感覚を引きずると調整に失敗しがちです。役割の分かれ方を一度テーブルで整理しておきます。
| レイヤー | mod_php時代 | fpm連携 (mod_proxy_fcgiなど) |
|---|---|---|
| Apache | HTTP受付とPHP実行の両方 | HTTP受付とリバースプロキシ |
| fpm | 不使用 | PHP実行プロセスの起動・管理 |
| チューニングの主戦場 | ApacheのMPM設定 | fpmのpm設定+ApacheのKeepAlive |
mod_phpでは「Apacheプロセス=PHPワーカー」でしたが、fpm連携ではApacheはリクエストを受け、PHP実行は別プロセスに投げるだけになります。この分離により、以下のような設計がしやすくなります。
-
静的ファイルはApacheで高速配信、PHPだけfpmへ
-
fpm側のpm.max_childrenとApache側のMaxRequestWorkersを別々に最適化
-
PHPの高負荷時にApache全体が巻き込まれにくい
mod_proxy_fcgiやProxyPassMatchでphpだけをfpmに流すルールを明示すると、「どのリクエストがPHPに行っているか」がログや設定から追いやすくなります。私の視点で言いますと、この「責任分担の見える化」が、リリース後のトラブル対応スピードを大きく変えます。
Apacheを再起動したらPHP-FPMも勝手に動く?よくある現場の勘違いを暴く
現場で本当によく出てくるのが、「httpdを再起動したら、なぜかphp側も調子が戻った」という体感から来る勘違いです。ここで押さえておきたいポイントをリストアップします。
-
多くのLinux環境では、Apacheとfpmは別々のsystemdサービス
-
Apache再起動で改善するのは、KeepAliveやワーカー数のリセットが主な理由
-
本当にPHP側が詰まっていた場合は、fpmサービスの再起動やpm設定見直しが必要
「Apacheを再起動するとphpも再起動される」と思い込むと、根本原因がfpmプールの枯渇やプロセスリークでも、毎回httpdだけ再起動してしまい、プロセスが残り続けてメモリをじわじわ食い尽くすパターンにつながります。
実務では、次のような切り分けフローを持っておくと安全です。
-
Apacheアクセスログのステータスとレスポンスタイムを見る
-
同じタイミングでfpmのerror_logとslowlog、プロセス数を確認する
-
Apacheだけ再起動しても改善しない場合は、fpm側のpm.max_childrenやrequest_terminate_timeoutを疑う
この「どのレイヤーを再起動すると何がリセットされるのか」を理解しておくと、再起動でごまかす運用から抜け出し、NginxやApacheとfpmを落ちないスタックとして育てていけます。
PHP-FPMのインストールや再起動とバージョン確認までUbuntuやDockerやWindowsで迷わない
インフラ運用をしている私の視点で言いますと、この章を押さえておくと「とりあえず動かす」フェーズから一気に脱出できます。環境ごとにコマンドや設定場所が微妙に違うため、そこでつまずいて本質的なチューニングにたどり着けない現場が非常に多いです。
UbuntuやCentOSでPHP-FPMをインストールする手順とインストール済みかすぐ確かめる方法
代表的なディストリの違いをまず整理します。
| OS系統 | インストール例 | サービス名の例 | 設定ファイルの例 |
|---|---|---|---|
| Ubuntu系 | apt install php-fpm | php8.1-fpm など | /etc/php/8.1/fpm/php-fpm.conf |
| CentOS系 | yum install php-fpm | php-fpm | /etc/php-fpm.conf |
「入っているか分からない」ときは次のいずれかを確認します。
-
systemctl status php-fpm
-
ps aux | grep php-fpm
-
dpkg -l php-fpm や rpm -qa | grep php-fpm
どれか1つで良いのではなく、プロセスとパッケージの両方を見て「インストール済みかつ起動中か」を切り分ける癖を付けておくと、後のトラブル調査がかなり楽になります。
PHP-FPMの再起動や停止や状態チェック、バージョン確認まで一気にマスター
運用で頻出する基本操作を表にまとめます。
| 操作 | systemd環境での例 | 補足ポイント |
|---|---|---|
| 再起動 | systemctl restart php-fpm | 設定変更後は必須 |
| 起動 | systemctl start php-fpm | 自動起動は enable も |
| 停止 | systemctl stop php-fpm | デプロイ時の一時停止など |
| 状態確認 | systemctl status php-fpm | エラー行を必ず読む |
| 自動起動設定 | systemctl enable php-fpm | 再起動後の取りこぼし防止 |
バージョン確認では php と FPMの両方を意識します。
-
php -v でPHPエンジンのバージョン
-
php-fpm -v でプロセスマネージャ側のバージョン
ここがズレている環境も実際にあり、モジュール互換性や設定ディレクトリの場所を誤解する原因になります。php.ini の場所も php –ini で一度は必ず確認しておくと安全です。
DockerでPHP-FPMのコンテナを立ち上げる時9000ポートやdocker-composeの鉄板レシピ
コンテナ利用時に多い事故は「どの9000番なのか分からなくなる」ケースです。FPM側とnginx側の対応を、composeファイルのレベルで明示しておくのがコツです。
-
FPMコンテナの listen を 9000ポートまたは unixソケットに統一
-
nginxコンテナの fastcgi_pass を fpm:9000 のように サービス名とポート で指定
-
開発環境でホストに公開したい場合のみ ports: – “9000:9000” を使う
ありがちな落とし穴は、portsを開けたのにnginxからは unixソケットに向けていたり、その逆をやってしまうパターンです。コンテナ間通信は「サービス名とポート」でつなぐ、ホストアクセスは「portsで外に出す」と役割を分けて考えると破綻しにくくなります。
Windowsや共有ホスティングでPHP-FPMを扱う時ブラックボックス化させないコツ
Windowsや共有ホスティングでは、FPMの起動方法や設定ファイルの場所がユーザーから見えにくく「ブラックボックス化」しがちです。ここでのポイントは、サーバーを完全には触れなくても、次の情報までは必ず自分で把握しておくことです。
-
どのPHPバージョンのFPMが動いているか
-
phpinfoで FPM/FastCGIモードかどうか
-
php.ini のパスと、memory_limit や max_execution_time の値
-
error_log の出力先 (アプリ側かFPM側か)
共有ホスティングで「FPMをオンにしたのに速くならない」ケースでは、プロセス数よりもWordPressプラグインやキャッシュ設定がボトルネックになっていることがよくあります。FPMのステータスを完全には覗けない環境ほど、phpinfoとアプリ側ログからボトルネックを推理する力がものを言います。ここを押さえておくと、環境を変えた時にも迷いがぐっと減ります。
PHP-FPM設定の本質に迫るwww.confやpm.start_serversをどう決めるか
「とりあえず動く」設定を卒業して、本番で落ちない設定に持っていくゾーンです。ここを押さえると、負荷が上がった瞬間の挙動がガラッと変わります。
PHP-FPMの設定ファイルの場所やチェックポイント最初に必ず見るべきポイント
まず全体像を押さえないと、www.confを触るたびに不安になります。代表的なパスは次の通りです。
| OS/パッケージ | メイン設定 | プール設定例 |
|---|---|---|
| Debian系 (Ubuntuなど) | /etc/php/X/fpm | /etc/php/X/fpm/pool.d/www.conf |
| RHEL系 (CentOSなど) | /etc/php-fpm.d | /etc/php-fpm.d/www.conf |
| ソースからインストール系 | /usr/local/etc | /usr/local/etc/php-fpm.d/www.conf |
X はバージョン番号です。service や systemctl で動いている unit 名からパスを追うと確実です。
最初に必ず確認したいポイントは次の4つです。
-
listen とその権限 (unix ソケットか :9000 ポートか、owner/group/mode)
-
user と group (Apache や nginx と同じか、必要なディレクトリにアクセスできるか)
-
pm 関連 (pm、pm.max_children、pm.start_servers など)
-
php_admin_value 系で php.ini を上書きしていないか
ここを押さえるだけで、「権限で 502」「ソケットに届かない」という初歩的な事故はかなり防げます。
pmモードやpm.max_childrenやpm.start_serversを「勘」じゃなく根拠でズバリ決める方法
本当に差が出るのは pm 設定です。業界人の目線で言うと、ここを“メモリ設計”として扱えるかどうかで、負荷ピーク時の安定度が決まります。
基本の考え方はひとつだけです。
1プロセスあたりメモリ使用量 × pm.max_children を、有効メモリの範囲に収める
有効メモリは、OSと他サービスを引いた残りです。ここを越えると swap が発生し、レスポンスが一気に悪化します。
ざっくりの手順は次の通りです。
- 本番に近い負荷を少しだけかけて、top や ps で php-fpm プロセスの RSS 平均を確認
- 有効メモリを計算し、その7〜8割以内に収まるよう pm.max_children を決める
- アクセスの山谷を見て、pm.start_servers・pm.min_spare_servers・pm.max_spare_servers を調整
pm モード選択の目安を簡単にまとめるとこうなります。
| pm モード | 向いているケース | 注意点 |
|---|---|---|
| static | 常に高負荷、本番専用サーバー | メモリ見積もりがシビア |
| dynamic | 一般的な Web アプリ全般 | 迷ったらまずこれ |
| ondemand | アクセスがまばら、開発・バッチ中心 | 初回アクセスの遅延に注意 |
私の視点で言いますと、最初から max_children をギリギリまで攻めて失敗する現場が本当に多いです。まずは安全寄りの値から始めて、status ページやログを見ながら少しずつ上げる方が結果的に早く安定します。
php.iniの設定がPHP-FPMのメモリやタイムアウトにどう影響するか関係性を徹底解説
www.conf と php.ini をバラバラに考えると、必ずどこかで齟齬が出ます。特に影響が大きいのは次の項目です。
-
memory_limit
-
max_execution_time
-
max_input_time
-
opcache 関連 (opcache.memory_consumption など)
それぞれ、FPM 側との関係はこうなります。
| php.ini 項目 | FPM に効くポイント |
|---|---|
| memory_limit | 1プロセスあたりメモリ量の上限を直撃 |
| max_execution_time | 子プロセスがどれだけ長くリクエストを握るか |
| max_input_time | POST が詰まるとプロセスが待たされる時間 |
| opcache.memory〜 | プロセス共通のメモリだが総量設計に影響 |
特に注意したいのは、memory_limit を上げたのに pm.max_children を据え置きにするパターンです。1プロセスが太った分だけ、同時に走らせていい本数を減らす必要があります。
逆に、max_execution_time よりも FPM の request_terminate_timeout を短くしてしまうと、「PHP スクリプトとしてはまだ生きているが、FPM が先に切る」という謎のタイムアウトに見えます。タイムアウト系は「アプリ側」「FPM」「nginx/Apache」の3段階で整合を取ることが、地味ですが効く設計ポイントです。
このあたりを押さえておくと、Laravel や WordPress で重い処理を流すときにも、「どこで詰まっているのか」を冷静に切り分けられるようになります。負荷が上がった瞬間に慌てないための“仕込み”として、今のうちに自分の環境の値を一度棚卸ししてみてください。
PHP-FPMのログやstatusページから「今起きていること」を見抜くトラブル診断の極意
本番が重いとき、設定をいじる前に「何が起きているか」を言語化できるかどうかで、腕前が一気に分かれます。この章では、ログとstatusページを武器にして、勘ではなく証拠でトラブルをねじ伏せる視点を整理します。
PHP-FPMのエラーログやslowlogでパフォーマンスの悲鳴を読み解くテク
まず押さえたいのは、どのログで何が分かるかを整理しておくことです。
| 種類 | 主な場所の例 | 分かること |
|---|---|---|
| エラーログ | /var/log/php-fpm/error.log など | 起動失敗、設定ミス、タイムアウト、OOMの痕跡 |
| slowlog | /var/log/php-fpm/slow.log など | どのスクリプトが何秒ハマっているか |
| Webサーバーログ | nginx, Apache のアクセス/エラー | 502/504の発生タイミングやURL |
特にslowlogは「アプリのどこが重いか」を炙り出すレントゲンです。
pm.max_childrenを増やす前に、slowlogで同じURLが何度も長時間実行されていないかを確認すると、ボトルネックがPHPコード側なのか、プロセス数なのか切り分けしやすくなります。
私の視点で言いますと、statusページとslowlogをオフにしたままチューニングを語る現場は、スピードメーターを見ずに車を運転しているのと同じ状態だと感じます。
PHP-FPMが起動しないや動かないやプロセスが残る時 症状別チェックで即対応
「動かない」ときは、症状ごとにチェックポイントを固定しておくと調査が一気に速くなります。
-
起動しない場合
- error.log に
unknown directiveやpool関連のエラーがないか - 設定ファイルの構文チェックを実行したか
- listenポートやソケットの権限がWebサーバーユーザーと合っているか
- error.log に
-
リクエストを受け付けない場合
- statusページで active / idle プロセス数を確認し、max_children に張り付いていないか
- Webサーバー側のfastcgi設定でパスやソケット名が一致しているか
-
プロセスが残り続ける場合
- pm.mode が static で、意図せず常駐しすぎていないか
- max_requests を0のままにしておらず、メモリリークで肥大化していないか
- OS側でゾンビプロセスになっていないかをpsで確認したか
この「症状 → 見るログ → 試すアクション」をテンプレ化しておくと、夜中の障害対応でも迷わず動けます。
502 Bad Gatewayや504が出たとき nginxとPHP-FPMのどこから攻めれば勝てるか
502や504は、WebサーバーかFPMか、どちらから崩していくかの順番を決めておくと強いです。
-
ステップ1: nginx / Apache のエラーログ
- タイムアウトなのか、ソケット接続失敗なのかメッセージを確認
- タイムアウトなら、上流のFPM側の処理時間・タイムアウト値を確認
-
ステップ2: FPMのエラーログ+statusページ
server reached pm.max_childrenが出ていないか- statusページで処理中プロセスの数と、キューの深さを確認
- 同時にslowlogで、特定のスクリプトが長時間居座っていないかを照合
-
ステップ3: アプリとインフラの境界を決める
- max_children に余裕があり、slowlogで特定URLだけ長いならアプリ改修が優先
- どのリクエストも並列時に詰まるなら、プロセス数の見直しやDB・外部APIのレイテンシを計測
慣れてくると、ログ1画面で「これはFPMの頭打ち」「これはDB待ち」と直感できるようになります。設定値をいじるより先に、ログとstatusページで今のボトルネックを言葉にしてから手を動かすことが、安定運用への一番の近道です。
WordPressやLaravelでNginxとPHP-FPM構成詰まりポイントと鉄板設定を大公開
WordPressやLaravelは「とりあえず動く」までは早いのに、本番でだけ妙に遅い・落ちる・502が出ることが多いです。ここでは現場で何度も踏まれている地雷と、その回避パターンを一気に整理します。
WordPressをPHP-FPMとNginxで動かすときlocation設定やFastCGIキャッシュの勝ち定番
WordPressはlocationが崩れると、404やリダイレクトループ地獄にハマります。ベースは次の形が安定です。
/location / の鉄板例/
-
rootはドキュメントルート直下
-
indexにindex.phpを必ず含める
-
try_filesでphp実行まで一気通貫
FastCGIキャッシュを噛ませる場合、「ログイン中はキャッシュしない」が鉄板です。
キャッシュ判定の典型例(考え方):
-
Cookieにwordpress_logged_in_があればキャッシュ無効
-
管理画面(/wp-admin/ /wp-login.php)は常に非キャッシュ
-
それ以外だけfastcgi_cacheを有効
この切り分けができていないと、管理画面がキャッシュされて「ログアウトできない」「更新が反映されない」といった事故が起きます。
LaravelのNginx設定やNginxとPHP-FPMのLaravel構成でハマりがちな権限・タイムアウト
Laravelはpublicディレクトリ配下だけをWeb公開するのが前提です。
代表的な詰まりポイントを整理すると次の通りです。
| 項目 | よくあるミス | 安定させるコツ |
|---|---|---|
| root | プロジェクト直下に設定 | publicをrootにする |
| location | /index.phpに直書き | /index.phpへのrewriteで一本化 |
| 権限 | storageがroot所有 | php実行ユーザーに書き込み権限 |
| タイムアウト | Nginxだけ短く設定 | NginxとFPMとphp.iniを整合させる |
特に権限とタイムアウトは本番で露骨に表面化します。権限はstorageとbootstrap/cacheをwwwユーザーに合わせ、タイムアウトは以下をセットで見ると迷いにくくなります。
-
Nginxのfastcgi_read_timeout
-
FPMプールのrequest_terminate_timeout
-
php.iniのmax_execution_time
どれか1つだけ極端に短いと、ピーク時に504が散発します。
DockerでPHP-FPMとLaravel構成コンテナ内ではOKなのに本番でコケるを防ぐ工夫
Docker構成では「コンテナ内とホストの差異」がボトルネックになりやすいです。私の視点で言いますと、本番トラブルのかなりの割合が以下3点に集約されます。
-
FPMコンテナとNginxコンテナのソケット/ポートの不一致
- Nginx側のfastcgi_passが127.0.0.1:9000のままになっている
- 実際にはphp-fpmというサービス名:9000で待ち受けている
-
volumeのマウント漏れでログ・storageだけ実ファイルとズレる
-
コンテナ内メモリとホストの制限を見誤り、pm.max_childrenだけ増やしてOOMを誘発
本番前に最低限チェックしたいポイントをリストにまとめます。
-
docker-composeでNginxとFPMが同じネットワークにいるか
-
fastcgi_passにコンテナ名:ポートを指定しているか
-
storageとlogsディレクトリをvolumeマウントしているか
-
コンテナ側メモリ制限とpm.max_childrenの組み合わせが妥当か
この辺りを設計段階で押さえておくと、「ローカルでは速いのに本番だけ重い」をかなりの確率で封じ込められます。
動いてから差がつくPHP-FPMチューニング体感を変えるリアルな調整術
「とりあえず動く」状態から「ピークでも落ち着いて見ていられる」状態に持っていくかどうかは、この章のチューニングでほぼ決まります。Webサーバーのレスポンスを、数字と体感の両方で変えていきましょう。
1プロセスごとのメモリから逆算するpm.max_childrenやpm.start_servers設定のリアル目安
私の視点で言いますと、pm設定は感覚ではなく「1プロセスあたりメモリ」を起点に決めた人から安定運用に近づきます。手順はシンプルです。
- 本番と同じコードでベンチマークを実行
- topやpsでphpプロセスのRSS(実メモリ)を確認
- その値をベースにmax_childrenを逆算
目安を表にすると次のようになります。
| 1プロセスあたりメモリ | 有効メモリ(OSや他サービスを除いた残り) | 現実的なpm.max_childrenの目安 |
|---|---|---|
| 80MB | 4GB | 30〜40 |
| 120MB | 4GB | 20〜25 |
| 200MB | 8GB | 25〜30 |
| 250MB | 8GB | 18〜22 |
pm.start_serversは「ピーク時の3〜5割」を狙うとスムーズです。たとえばmax_childrenが30なら、start_serversは10〜15、pm.min_spare_serversは5〜8程度から始めると、アイドル時の無駄を抑えつつ、急なスパイクにもある程度耐えられます。
PHP-FPMチューニングでありがちな「max_children盛りすぎ事故」と実際の副作用
よくあるのが「サーバーメモリを全部使い切る設計」にしてしまい、ピーク時にswapやOOMに突入するパターンです。起きる症状はとても派手ですが、原因は地味です。
-
max_childrenをメモリ上限近くまで上げる
-
WordPressやLaravelの機能追加で1プロセスあたりメモリがじわじわ増える
-
アクセス集中でプロセスが総動員される
-
OSがswapを使い始めてディスクIOが増え、レスポンスが一気に悪化
-
最終的にOOM Killerがphpやmysqldを落とし、502や504が断続的に発生
怖いのは、アクセスログを見ると「リクエストは来ている」ように見える点です。max_childrenを欲張るほど同時処理は増えますが、1リクエストあたりが極端に遅くなるため、ユーザー体感はむしろ悪化します。
安全側に倒したい場合は、有効メモリの7割くらいを上限に逆算し、残りをOSとファイルキャッシュ、mysqld、nginxやhttpdのために空けておくと、長期運用での事故をかなり減らせます。
statusページやslowlogを常時オンにして“現場力”を底上げするこだわり運用法
チューニングの成否は、「原因を切り分ける道具」を仕込んでいるかどうかで変わります。statusとslowlogを常時有効にしておくと、次のような判断がしやすくなります。
-
現在稼働中のプロセス数とキューの長さから、max_children不足かを判断
-
特定URLや特定SQLで処理が詰まっているかをslowlogで特定
-
nginxやApacheのタイムアウト設定と、php側の実行時間上限のどちらが先に切れているかを把握
実運用でおすすめの運用ルールは次の通りです。
-
statusページはIP制限やbasic認証をかけた上で常時公開
-
slowlogは1秒〜3秒程度から開始し、アプリが安定してきたら閾値を徐々に下げる
-
デプロイ後最初のピークは、statusとエラーログを開いたまま監視
-
月に1回はslowlogの上位URLを洗い出し、ボトルネックを優先的に改善
fastcgiの世界では、「何が遅いか分からない」が一番のコストです。phpのコード、データベース、ファイルIO、そのどこで時間を使っているのかを可視化しておけば、pm設定を闇雲にいじって迷走するリスクをしっかり減らせます。
現場で出会ったリアルなPHP-FPMトラブルともう再発させない予防思考
「なぜ本番だけ落ちるのか」を説明できるようになると、一気に評価されます。この章では、実際の現場で頻発する3パターンから、再発させないための“考え方”をまとめます。
昼ピークでCPUが張り付く激重になった現場とpm設定での立て直し劇
昼だけCPUが100%に張り付き、topを見るとphpのプロセスだらけ。よくあるのは、pm.max_childrenを「多いほど速い」と誤解して上げすぎたケースです。
私の視点で言いますと、この手の事故現場では、まず次のような簡易表を作ります。
| 観点 | 悪い例 | 立て直し方 |
|---|---|---|
| pm.max_children | メモリ無視で大きく設定 | 1プロセスあたりのメモリを実測し逆算する |
| pm.start_servers | 適当に大きめ | 通常負荷時の平均同時実行数程度に抑える |
| pm.max_requests | デフォルト放置 | メモリリーク疑いがあれば小さめに調整 |
ポイントは、「1プロセスあたりメモリ」×「pm.max_children」≤「サーバで使ってよいメモリ」を守ることです。
都度、本番と同じphp.iniとアプリコードで、abやwrkなどを使い1プロセスのRSSを計測し、上限を決めると、CPUもメモリも安定しやすくなります。
さらに、statusページを有効化して、ピーク時の「現在処理中のリクエスト数」「アイドルプロセス数」を記録しておくと、増強が必要なのか、アプリ側の最適化が先なのか判断しやすくなります。
深夜リリース後の502連発案件で見逃されていたメモリとOOMとの意外な関係
深夜のリリース直後だけ502が断続的に発生し、朝になると落ち着く案件では、OSのOOM Killerにphp-fpmが落とされていることが少なくありません。
典型的な見落としは次の流れです。
-
Nginxのエラーログには upstream prematurely closed connection だけが残る
-
php-fpmのerror_logには明確な致命的エラーが出ていない
-
実はdmesgやsyslogに Out of memory と Killed php-fpm が記録されている
このパターンでは、リリース後のキャッシュ再生成やマイグレーションで一時的にメモリ消費が跳ね上がり、平時には安全だったpm.max_childrenが「その瞬間だけ危険な値」になることが多いです。
予防としては、次のようなチェックリストを用意しておくと安定します。
-
リリース直後に走るバッチやキュー処理が同じサーバで動いていないか
-
それらのプロセスとphpの合計メモリを見た上で、pm.max_childrenを再計算したか
-
OOM発生時に備え、monitoringツールでメモリとプロセス数の履歴を取っているか
また、php.iniのmemory_limitを無制限に近い値にしていると、1プロセスあたりメモリが膨張しやすく、同じpm設定でも危険度が一気に上がります。メモリ設計とpm設計をセットで見る意識が重要です。
共有レンタルサーバでPHP-FPMをONにしたのに速くならなかった時のWordPress側見直し技
共有レンタルサーバやcPanel環境で、管理画面からfpmを有効化したのに、期待していたほどWordPressが速くならない相談も多いです。この場合、ボトルネックはPHPではなく、アプリとプラグイン構成にあることがよくあります。
ありがちな原因を整理すると次の通りです。
| 層 | 典型的なボトルネック | 見直しポイント |
|---|---|---|
| PHP層 | プロセス数不足 | プラン依存のため上限を把握 |
| アプリ層 | 重いプラグイン・テーマ | 不要プラグイン削除、クエリ削減 |
| DB層 | インデックス不足 | スロークエリログを確認 |
| キャッシュ層 | ページキャッシュ未設定 | プラグインまたはNginx側でキャッシュ構成 |
共有環境ではpm設定を細かく触れないことが多いので、WordPress側で「PHPに負荷をかけない設計」に寄せることが一番効きます。具体的には、次の順番で見直すと効果が読みやすくなります。
-
管理画面のプラグイン一覧で、使っていないプラグインを停止・削除
-
クエリモニタ系プラグインで、遅いSQLを洗い出し、不要な機能をオフ
-
ページキャッシュやオブジェクトキャッシュを有効にし、PHP実行回数そのものを減らす
NginxやApacheとfpmの組み合わせは強力ですが、「PHP層だけをチューニングしても、アプリ層が詰まっていれば速くならない」という前提を持っておくと、遠回りを避けられます。CPUやメモリのグラフとあわせてWebサーバログ、php-fpmログ、そしてアプリログを縦串で眺める習慣が、再発防止の一番の近道になります。
この記事で目指すゴールPHP-FPMを「運用できる武器」に変えるためのまとめ
「なんとなくNginxでphpを動かしているサーバー」を、ログを読み、pm設定を触り、本番トラブルを自分でさばける環境に引き上げることがゴールです。ここだけ読み返せば、明日からの運用の指針になるように整理します。
読み終わったその日からできるPHP-FPMチェックポイント完全リスト
まずは、今日すぐに確認できるポイントを一覧にします。
-
プロセスとバージョンを把握する
systemctl status php-fpm*でservice名と起動ユーザーを確認php-fpm -vまたはphp -vでFPMのバージョンを把握
-
設定ファイルの場所とpm設定を押さえる
/etc/php/*/fpm/php-fpm.conf/etc/php/*/fpm/pool.d/www.confのpm,pm.max_children,pm.start_serversを控える
-
1プロセスあたりメモリ使用量をざっくり測る
- アクセスが乗っている時間帯に
ps aux | grep php-fpmでrssを確認 - 「rssの平均 × pm.max_children」が物理メモリの6~7割を超えていないかチェック
- アクセスが乗っている時間帯に
-
ログとstatus・slowlogを有効化する
- FPMのerror_logが
/var/log/php-fpm/error.logなどに出ているか request_slowlog_timeoutとslowlogをwwwプールに設定しているかpm.status_path = /statusを設定し、nginxやApacheからlocalhost限定で参照できるようにする
- FPMのerror_logが
-
Webサーバーとの接続方式を確認する
- Nginxなら
fastcgi_pass unix:/run/php-fpm/www.sock;か127.0.0.1:9000かを確認 - Apacheなら
ProxyPassMatchやSetHandler "proxy:unix:/path/to.sock|fcgi://localhost"になっているか確認
- Nginxなら
-
502/504時の調査フローを決めておく
- まずnginxやApacheのerror_log
- 次にFPMのerror_log
- 最後にアプリ側のlog(Laravelのstorage/logsやWordPressプラグインのlog)という優先順位を決めておく
-
DockerやWindows環境の“境界”を意識する
- Dockerなら
docker-compose.ymlのport 9000公開有無とvolumeマウントを整理 - Windowsや共有ホスティングでは、どこまでが自分で触れる設定かをドキュメントで明確にする
- Dockerなら
上のチェックを日常的に使う観点でまとめると、次のようになります。
| 観点 | 確認場所・コマンド | OKラインの目安 |
|---|---|---|
| 動作状態 | systemctl status php-fpm |
Active running でエラーが出ていない |
| 接続方式 | nginx.conf, httpd.conf, vhost設定 | unixソケットか127.0.0.1:9000で一貫している |
| メモリ設計 | ps aux と www.conf のpm関連 |
rss平均×max_childrenが物理メモリの6割前後 |
| タイムアウト | php.ini, nginx/apacheのfastcgi設定 | PHPとWebサーバーのtimeout値が矛盾していない |
| ログ | /var/log/php-fpm, Webサーバーのerror_log |
502/504時に同じ時刻のエラーが追える |
| status/slowlog運用 | www.conf, Webサーバーのlocation設定 | 本番でも限定公開で常時有効化している |
PHP-FPMを軸に設計・ログ分析・チューニングを明日から実践で活かす方法
ここからは、単なるチェックにとどめず「武器」として使うための考え方です。
1つ目は、pm設定をメモリから逆算する癖をつけることです。1プロセスあたりメモリが重いLaravelやWordPressでは、pm.max_childrenを欲張るほどswapやOOMに近づきます。ピーク時にps auxやtopでrssを眺め、「このアプリは1プロセスだいたいこれくらい」という体感を数字にしておくと、サーバー増強やリプレースの相談も説得力が増します。
2つ目は、statusページとslowlogを“常時計測器”として扱うことです。statusでは今のプールのbusy/idle数や、処理中リクエストの数が分かります。slowlogでは、Laravelの特定ルートやWordPressのプラグインがどのファイル・行で時間を食っているかが浮き彫りになります。闇雲にpm.max_childrenを増やす前に、「本当にFPM側がボトルネックなのか」「アプリコードにボトルネックがあるのか」を切り分ける軸になります。
3つ目は、スタックごとの“お作法”を押さえることです。
-
Nginx+FPM+WordPressなら、locationの書き方とFastCGIキャッシュでPHPプロセス自体へのリクエストを減らす
-
Nginx+FPM+Laravelなら、storageディレクトリの権限とqueue・cronとの兼ね合いを整理しておく
-
Docker構成なら、本番と手元のport, volume, envが揃っているかをcomposeファイルで機械的に保証する
最後に、私の視点で言いますと、FPMを「黒魔術の設定ファイル」から「ふるまいが見えるプロセスマネージャ」として扱えるようになった瞬間に、502やタイムアウトの調査スピードが一気に変わります。pm設定とメモリ、statusとslowlog、Webサーバーとのfastcgi連携。この3本柱を押さえておけば、明日からの運用で一段上の安心感を手に入れられます。
この記事を書いた理由
著者 – 宇井 和朗(株式会社アシスト 代表)
PHP-FPMの記事を書こうと思ったのは、「サーバは動いているのに売上だけが止まる」瞬間を何度も見てきたからです。ある案件では、キャンペーン開始直後のアクセス集中で502と504が連発し、広告と人件費をかけて集めたユーザーが数分で離脱していきました。原因は、NginxとPHP-FPMの連携設計とpm.max_childrenの決め方を、過去の感覚だけで流用してしまったことでした。
私は創業期から、自社と支援先のサイト運用で、WordPressやLaravel、Docker本番環境まで含めてPHP-FPMのトラブルに何度も向き合ってきました。共通していたのは、「インストール手順は分かるが、本番で落とさない設計とログによる検証が弱い」という点です。
この記事では、インフラ担当がいない中小企業でも、NginxやApacheとの構成、www.confとphp.ini、statusやslowlogの読み方までを一気通貫で押さえ、本番で同じ失敗を繰り返さないための考え方をまとめました。運営側の立場で信用と売上を守るために、私が現場で必要だと痛感してきた内容だけを書いています。