2026年6月17日

2026年6月17日

VPSでPHP-FPMを最適化する方法|pm.max_childrenの計算・ステータス画面・slowlogで根拠を持って調整する

VPSでのPHP-FPMの最適化は、「1プロセスあたりのメモリを実測し、使えるメモリから pm.max_children を逆算する」ことから始めます。そのうえでステータス画面とslowlogで、足りないのがプロセス数なのか、遅いPHPコードなのかを見分けます。この記事では、WordPressを動かす一般的なVPSを例に、設定値の決め方と効果の確かめ方を説明します。

設定ファイルの場所と反映方法

PHP-FPMの設定は、本体の php-fpm.conf と、プールごとの設定ファイルに分かれています。チューニングで触るのは主にプール設定です。

  • Debian / Ubuntu:/etc/php/8.3/fpm/pool.d/www.conf(8.3 の部分はインストールされているPHPのバージョン)
  • AlmaLinux / Rocky Linux などRHEL系:/etc/php-fpm.d/www.conf

Ubuntu 24.04の標準パッケージはPHP 8.3です(2026年10月現在)。複数バージョンを入れている場合は、Webサーバーがどのバージョンのソケットに接続しているかを先に確認しておかないと、関係のないプールを調整してしまいます。

変更後は、構文チェックをしてから再読み込みします。

sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm

reload は処理中のリクエストを終えてからワーカーを入れ替えるため、通常は restart より安全です。RHEL系ではコマンド名が php-fpm -t、サービス名が php-fpm になります。

プロセス管理モード(pm)を選ぶ

pm は子プロセス(ワーカー)の増減のさせ方を決める設定で、3種類あります。

  • static:常に pm.max_children 個のワーカーを起動しておきます。応答は安定しますが、アクセスが少ない時間もメモリを占有します
  • dynamic:待機中のワーカー数を pm.min_spare_servers 〜 pm.max_spare_servers に保ちながら、最大 pm.max_children まで増減します。多くのディストリビューションの既定値です
  • ondemand:リクエストが来たときにワーカーを起動し、pm.process_idle_timeout(既定10秒)アイドルが続くと終了させます。メモリは節約できますが、アクセスが来た瞬間に起動の待ちが入ります

WordPress専用のVPSでメモリに余裕があるなら dynamic か static、1台に多数の小さなサイトを載せて、それぞれのアクセスがまばらなら ondemand が向いています。

pm.max_children をメモリから計算する

最も重要な値が pm.max_children、つまり同時に処理できるPHPリクエストの上限です。小さすぎるとアクセスが集中したときに待ち行列ができ、大きすぎるとメモリが尽きてスワップやOOM Killerによる強制終了が起こります。

まず、実際に動いているワーカーの平均メモリ使用量を測ります。サイトに普段どおりのアクセスがある時間帯に実行してください。

ps --no-headers -o rss -C php-fpm8.3 | awk '{sum+=$1; n++} END {printf "%d processes, avg %.1f MB\n", n, sum/n/1024}'

RSSにはOPcacheなどの共有メモリが各プロセス分重複して数えられるため、実際の消費より大きめに出ます。安全側の見積もりとしてはこの値で問題ありません。より正確に知りたい場合は smem コマンドでPSS(共有部分を按分した値)を確認します。

次に、PHP-FPMに割り当てられるメモリを決めます。メモリ4GBのVPSでMySQLとNginxが同居する例で考えます。

  • OS・Nginx・その他:約0.5GB
  • MySQL(InnoDBバッファプールなど):約1GB
  • 余裕(ファイルキャッシュ、急な増加への備え):約0.5GB
  • PHP-FPMに使える分:約2GB(2,048MB)

平均が80MBなら、2,048 ÷ 80 ≒ 25.6 なので、pm.max_children = 25 が上限の目安です。WordPressで重いプラグインを使っていたり、管理画面で画像処理をしたりすると1プロセスが150MB以上になることもあるため、計算は自分の環境の実測値で行ってください。

; /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 25
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500

dynamic の補助値は、待機ワーカーを少し持っておく程度で十分です。pm.start_servers は pm.min_spare_servers と pm.max_spare_servers の間の値でなければならず、範囲外だと起動時にエラーになります。

pm.max_requests は、1つのワーカーがこの回数のリクエストを処理したら自動で作り直す設定です。既定は0(作り直さない)ですが、プラグインのメモリリークでワーカーが徐々に太っていくのを抑えるため、数百程度にしておくのが一般的です。

エラーログで上限到達を見つける

pm.max_children が足りないと、PHP-FPMのエラーログ(Debian/Ubuntuでは既定で /var/log/php8.3-fpm.log)に次のような警告が出ます。

WARNING: [pool www] server reached pm.max_children setting (25), consider raising it
WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 12 total children

1つ目は同時処理の上限に達したという意味です。ただし、これを見てすぐに値を上げるのは早計です。上限に達した原因が「アクセスが本当に多い」のか「1リクエストが遅くてワーカーが詰まっている」のかで対処が変わるからです。後者の場合、上限を上げても詰まるワーカーが増えるだけで、メモリが先に尽きます。この見分けにステータス画面とslowlogを使います。

2つ目は、待機ワーカーが足りず急いで増やしているという意味で、pm.min_spare_servers を少し上げれば落ち着くことが多い警告です。

ステータス画面で混雑を数値で見る

プール設定でステータスページを有効にします。

pm.status_path = /fpm-status
ping.path = /fpm-ping

Nginxでは、外部から見えないようにアクセス元を限定して公開します。

location ~ ^/(fpm-status|fpm-ping)$ {
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

サーバー上から curl http://127.0.0.1/fpm-status を実行すると、次のような項目が確認できます(server_name の設定によっては -H 'Host: example.com' を付けます)。

  • listen queue:今、空きワーカーを待っているリクエスト数。0が正常です
  • max listen queue:起動以降の待ち行列の最大値
  • max children reached:pm.max_children に達した回数
  • active processes / idle processes:処理中と待機中のワーカー数
  • slow requests:slowlogの基準時間を超えたリクエスト数

max children reached が増え続けていて slow requests が少なければ、純粋に同時処理数が足りていないので、メモリの計算の範囲内で pm.max_children を上げます。slow requests も増えているなら、先に遅い処理を特定します。?full を付けると、ワーカーごとの処理中URIと経過時間も表示されます。

slowlogで遅いPHPコードを特定する

request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm.slow.log

5秒を超えたリクエストについて、その時点で実行中だった関数のスタックトレースがslowlogに記録されます。WordPressであれば、トレースに出てくるファイルのパスから、どのプラグインやテーマのどの処理(外部APIへの通信、重いクエリなど)で止まっているかがわかります。

あわせて、処理が止まったワーカーを強制終了する request_terminate_timeout も設定しておくと、1つの暴走リクエストがワーカーを占有し続けるのを防げます。この値はPHPの max_execution_time とは別に働き、Nginx側の fastcgi_read_timeout より短いと、Nginxが502エラーを返す原因になります。3つの値の関係を揃えてから設定してください。

OPcacheと接続方式も見直す

プロセス数と並んで効果が大きいのがOPcacheです。PHPファイルをコンパイルした結果を共有メモリに保持するため、WordPressのように多数のファイルを読み込むアプリでは、CPU使用率と応答時間の両方が下がります。opcache.memory_consumption と opcache.max_accelerated_files が小さすぎるとキャッシュが溢れて効果が落ちるため、opcache_get_status() などでヒット率と空き容量を確認します。

WebサーバーとPHP-FPMが同じVPS上にあるなら、TCP(127.0.0.1:9000)よりUnixソケット(/run/php/php8.3-fpm.sock)のほうがオーバーヘッドが小さくなります。ソケットを使う場合は listen.owner、listen.group、listen.mode で、Nginxの実行ユーザーが読み書きできるようにしておきます。

調整後に確認すること

設定を変えたら、次の順で効果を確かめます。

  1. free -h と vmstat 5 で、ピーク時にスワップが使われていないか
  2. ステータス画面で listen queue が0に近く、max children reached が増えなくなったか
  3. エラーログに server reached pm.max_children が出なくなったか
  4. 実際のページの応答時間(TTFB)が改善したか

一度に複数の値を変えると、どの変更が効いたのかわからなくなります。pm.max_children → slowlogで見つけた遅い処理 → OPcache、のように1つずつ変えて記録を残すのが、結果的に早道です。本番環境の設定ファイルは、変更前にコピーを取っておきます。

まとめ

  • pm.max_children は「PHPに使えるメモリ ÷ 1プロセスの実測メモリ」で上限を決める
  • メモリに余裕のある専用VPSは dynamic か static、小さなサイトが多数なら ondemand を選ぶ
  • server reached pm.max_children の警告が出ても、すぐに値を上げず、ステータス画面とslowlogで「処理数不足」か「遅いコード」かを見分ける
  • request_terminate_timeout、max_execution_time、Nginxの fastcgi_read_timeout の関係を揃える
  • OPcacheとUnixソケットも合わせて見直し、変更は1つずつ行って効果を測る

お気軽にご相談ください

お見積りへ お問い合わせへ
▲