PHP-FPM (FastCGI Process Manager): Kompletny przewodnik konfiguracji, ustawień i optymalizacji
PHP-FPM (PHP FastCGI Process Manager) to wysokowydajny alternatywny menedżer procesów PHP, który implementuje protokół FastCGI w celu oddzielenia wykonywania PHP od procesu serwera WWW. Zamiast uruchamiać nowy interpreter PHP dla każdego przychodzącego żądania HTTP — jak robi to tradycyjne CGI — PHP-FPM utrzymuje trwałą pulę procesów roboczych, które przyjmują, wykonują i zwracają odpowiedzi PHP ze znacznie mniejszym narzutem.
Dla każdego produkcyjnego serwera WWW obsługującego WordPress, Laravel, Symfony lub niestandardowe aplikacje PHP, PHP-FPM jest standardowym handlerem. Umożliwia precyzyjną kontrolę nad cyklem życia procesów, limitami pamięci, kolejkowaniem żądań i izolacją per-aplikacja — możliwości, które są po prostu niedostępne w mod_php ani zwykłym CGI.
Czym PHP-FPM różni się od CGI i mod_php
Aby zrozumieć, dlaczego PHP-FPM ma znaczenie, warto dokładnie zobaczyć, co zastępuje i dlaczego te alternatywy zawodzą przy dużej skali.
| Funkcja | CGI | mod_php | PHP-FPM |
|---|---|---|---|
| — | — | — | — |
| Model procesów | Nowy proces na żądanie | Wbudowany w Apache | Trwała pula procesów roboczych |
| Wydajność pamięci | Bardzo słaba | Umiarkowana | Doskonała |
| Powiązanie z serwerem WWW | Ścisłe | Ścisłe (tylko Apache) | Oddzielone (dowolny serwer) |
| Izolacja per-strona | Brak | Brak | Pełna (oddzielne pule) |
| Płynne przeładowanie | Nie | Nie | Tak |
| Slow log / profilowanie | Nie | Nie | Tak |
| Dynamiczne skalowanie procesów | Nie | Nie | Tak |
| Obsługa gniazd Unix | Nie | Nie | Tak |
| Kompatybilność z NGINX | Nie | Nie | Tak |
CGI tworzy nowy proces OS dla każdego żądania. Przy umiarkowanym ruchu powoduje to tysiące cykli fork/exec/exit na minutę, nasycając CPU i pamięć. mod_php osadza interpreter PHP bezpośrednio w każdym procesie roboczym Apache, co oznacza, że każdy proces Apache — nawet ten obsługujący statyczny obraz — nosi pełne środowisko uruchomieniowe PHP w pamięci. PHP-FPM rozwiązuje oba problemy: procesy robocze są trwałe i całkowicie oddzielone od serwera WWW, więc NGINX lub Apache obsługuje zasoby statyczne natywnie, podczas gdy PHP-FPM obsługuje tylko wykonywanie PHP.
Architektura PHP-FPM: Szczegółowy przepływ żądań
Zrozumienie wewnętrznej ścieżki żądań jest niezbędne do strojenia i debugowania.
- Przeglądarka wysyła żądanie HTTP dla zasobu
.php. - Serwer WWW (NGINX lub Apache) odbiera żądanie i dopasowuje je do bloku lokalizacji lub dyrektywy
FilesMatch. - Serwer WWW przekazuje żądanie do PHP-FPM za pośrednictwem protokołu FastCGI — przez gniazdo domeny Unix (
/run/php/php8.2-fpm.sock) lub gniazdo TCP (127.0.0.1:9000). - Proces główny PHP-FPM kieruje żądanie do dostępnego procesu roboczego ze skonfigurowanej puli.
- Proces roboczy wykonuje skrypt PHP, zapisuje do
stdouti zwraca odpowiedź do serwera WWW. - Serwer WWW dostarcza wyrenderowany HTML do klienta.
- Proces roboczy nie kończy działania — wraca do puli bezczynnych, gotowy na następne żądanie.
Gniazda Unix są preferowane nad TCP do komunikacji lokalnej, ponieważ całkowicie omijają stos TCP/IP, zmniejszając opóźnienia o 10–20% w testach porównawczych i eliminując narzut związany z wiązaniem portów i routingiem pętli zwrotnej.
Tryby zarządzania procesami
PHP-FPM obsługuje trzy tryby pm (menedżera procesów), a wybór niewłaściwego jest jednym z najczęstszych błędów konfiguracji.
pm = static
Stała liczba procesów roboczych jest zawsze uruchomiona, niezależnie od ruchu. Używaj tego na dedykowanych serwerach, gdzie chcesz przewidywalnej, wstępnie przydzielonej pamięci i możesz sobie pozwolić na narzut bezczynności.
pm = static
pm.max_children = 20pm = dynamic
PHP-FPM uruchamia bazową liczbę procesów roboczych i skaluje w górę lub w dół w określonych granicach. Jest to najczęściej używany tryb i właściwe ustawienie domyślne dla większości środowisk Hostingu VPS.
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500pm = ondemand
Procesy robocze są uruchamiane tylko po nadejściu żądania i kończone po pm.process_idle_timeout sekundach bezczynności. Minimalizuje to zużycie pamięci w stanie bezczynności i jest idealne dla stron o niskim ruchu lub środowisk współdzielonych, gdzie współistnieje dziesiątki pul.
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 10sKrytyczna pułapka: ondemand wprowadza opóźnienie zimnego startu przy pierwszym żądaniu po okresie bezczynności. W przypadku aplikacji wrażliwych na opóźnienia, dynamic jest zawsze lepszym wyborem.
Prawidłowe obliczanie pm.max_children
To tutaj większość administratorów popełnia kosztowne błędy. Ustawienie pm.max_children zbyt wysoko powoduje wyczerpanie pamięci i zabicia OOM; zbyt nisko powoduje kolejkowanie żądań i błędy 502 pod obciążeniem.
Prawidłowa formuła:
pm.max_children = (Available RAM for PHP) / (Average PHP worker memory usage)Aby znaleźć średnią pamięć procesu roboczego PHP:
ps --no-headers -o "rss,cmd" -C php-fpm8.2 | awk '{ sum+=$1 } END { printf "Average: %d MBn", sum/NR/1024 }'Na VPS z 2 GB RAM, gdzie NGINX, MySQL i system operacyjny zużywają ~600 MB, masz około 1 400 MB dla PHP. Jeśli każdy proces roboczy używa ~70 MB, bezpieczna wartość pm.max_children wynosi 20. Nigdy nie ustawiaj jej na podstawie domysłów.
Instalacja PHP-FPM
Debian / Ubuntu
sudo apt update
sudo apt install php8.2-fpm
sudo systemctl enable php8.2-fpm
sudo systemctl start php8.2-fpmCentOS / AlmaLinux / RHEL (z repozytorium Remi)
sudo dnf install epel-release
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module enable php:remi-8.2
sudo dnf install php-fpm
sudo systemctl enable php-fpm
sudo systemctl start php-fpmSprawdź, czy usługa działa i potwierdź ścieżkę gniazda:
sudo systemctl status php8.2-fpm
ls -la /run/php/Konfiguracja pul PHP-FPM
Główna konfiguracja PHP-FPM znajduje się w /etc/php/8.2/fpm/php-fpm.conf, ale definicje poszczególnych pul należą do /etc/php/8.2/fpm/pool.d/. W systemach opartych na RHEL pliki pul znajdują się w /etc/php-fpm.d/.
Każda pula jest izolowanym środowiskiem wykonawczym. Uruchamianie wielu aplikacji PHP na tym samym serwerze — na przykład strony WordPress i API Laravel — oznacza tworzenie oddzielnych plików pul z oddzielnymi użytkownikami, ścieżkami gniazd i limitami zasobów. Jest to właściwa architektura dla konfiguracji wielodostępnych i jest znacznie bezpieczniejsza niż współdzielenie jednej puli.
Przykład: Konfiguracja puli produkcyjnej
[myapp]
user = myapp
group = myapp
; Unix socket — always prefer this over TCP for local communication
listen = /run/php/myapp-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Process manager
pm = dynamic
pm.max_children = 30
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 1000
; Slow log — log requests taking longer than 2 seconds
slowlog = /var/log/php-fpm/myapp-slow.log
request_slowlog_timeout = 2s
; Status and ping endpoints
pm.status_path = /fpm-status
ping.path = /fpm-ping
; Environment isolation
clear_env = yes
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
; PHP value overrides per pool
php_admin_value[error_log] = /var/log/php-fpm/myapp-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64MDyrektywa clear_env = yes jest ustawieniem krytycznym dla bezpieczeństwa, które jest często pomijane. Bez niej procesy robocze PHP dziedziczą wszystkie zmienne środowiskowe z procesu głównego, potencjalnie ujawniając wrażliwe dane systemowe do $_ENV aplikacji.
Integracja PHP-FPM z NGINX
NGINX nie ma natywnej możliwości wykonywania PHP — polega całkowicie na FastCGI do delegowania żądań PHP. Jest to w rzeczywistości zaleta architektoniczna: NGINX obsługuje pliki statyczne przy prawie zerowym koszcie, podczas gdy PHP-FPM obsługuje tylko to, co wymaga wykonania.
server {
listen 80;
server_name example.com;
root /var/www/myapp/public;
index index.php index.html;
# Serve static files directly, no PHP involvement
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Delegate PHP to PHP-FPM
location ~ .php$ {
# Security: prevent executing uploaded files as PHP
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass unix:/run/php/myapp-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# Performance tuning
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 300;
}
# Block access to the FPM status page from public
location ~ ^/(fpm-status|fpm-ping)$ {
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/myapp-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}Uwaga dotycząca bezpieczeństwa: Linia try_files $uri =404; przed fastcgi_pass jest niezbędna. Bez niej NGINX będzie przekazywał żądania dla nieistniejących plików do PHP-FPM, umożliwiając ataki path traversal, gdzie atakujący przesyła obraz zawierający kod PHP i nakłania serwer do jego wykonania.
Integracja PHP-FPM z Apache
Apache wymaga mod_proxy_fcgi do komunikacji z PHP-FPM. W przeciwieństwie do mod_php, to podejście pozwala Apache uruchamiać PHP-FPM jako oddzielny użytkownik, poprawiając izolację.
sudo a2enmod proxy_fcgi setenvif
sudo systemctl restart apache2Konfiguracja wirtualnego hosta:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/myapp/public
<Directory /var/www/myapp/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch ".php$">
SetHandler "proxy:unix:/run/php/myapp-fpm.sock|fcgi://localhost/"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/myapp-error.log
CustomLog ${APACHE_LOG_DIR}/myapp-access.log combined
</VirtualHost>Włączanie i używanie strony statusu PHP-FPM
Wbudowana strona statusu jest jednym z najbardziej niedocenianych narzędzi diagnostycznych PHP-FPM. Po skonfigurowaniu pm.status_path w pliku puli, zapytaj ją bezpośrednio:
sudo -u www-data SCRIPT_NAME=/fpm-status SCRIPT_FILENAME=/fpm-status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/myapp-fpm.sockLub, bardziej praktycznie, przez curl po udostępnieniu jej na ograniczonym wewnętrznym punkcie końcowym:
curl http://127.0.0.1/fpm-status?fullKluczowe metryki do obserwowania:
listen queue: Żądania oczekujące na wolny proces roboczy. Każda wartość powyżej 0 przy stałym obciążeniu oznacza, żepm.max_childrenjest zbyt niskie.active processes: Procesy robocze aktualnie wykonujące PHP. Jeśli konsekwentnie równa siępm.max_children, jesteś na granicy wydajności.slow requests: Skumulowana liczba żądań, które przekroczyłyrequest_slowlog_timeout. Rosnąca liczba wskazuje na wąskie gardła na poziomie aplikacji.
Używanie slow log do debugowania wydajności
Slow log przechwytuje pełny stos wywołań PHP dla każdego żądania przekraczającego skonfigurowany próg. Jest to nieocenione do identyfikowania problemów z zapytaniami N+1, blokujących wywołań I/O lub nieefektywnych pętli bez potrzeby używania pełnego profilera.
slowlog = /var/log/php-fpm/myapp-slow.log
request_slowlog_timeout = 2sWpis slow log wygląda tak:
[21-Jun-2025 14:32:11] [pool myapp] pid 18432
script_filename = /var/www/myapp/public/index.php
[0x00007f3b4c001e80] PDOStatement->execute() /var/www/myapp/vendor/laravel/framework/src/Illuminate/Database/Connection.php:338
[0x00007f3b4c001d40] runQueryCallback() /var/www/myapp/vendor/laravel/framework/src/Illuminate/Database/Connection.php:295Natychmiast mówi to, że wąskim gardłem jest zapytanie do bazy danych, a nie logika PHP — precyzyjnie kierując wysiłek optymalizacji.
PHP-FPM z OPcache: Niezbędne połączenie
PHP-FPM sam w sobie obsługuje zarządzanie procesami; OPcache eliminuje koszt parsowania i kompilowania plików źródłowych PHP przy każdym żądaniu. Razem tworzą kompletny stos wydajnościowy dla PHP na Linux.
Włącz i dostosuj OPcache w /etc/php/8.2/fpm/php.ini lub dedykowanym /etc/php/8.2/fpm/conf.d/10-opcache.ini:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.jit_buffer_size=128M
opcache.jit=tracingUstawienie validate_timestamps=0 wyłącza sprawdzanie modyfikacji plików przy każdym żądaniu — znaczący wzrost wydajności w środowisku produkcyjnym. Podczas wdrażania nowego kodu wyzwól reset pamięci podręcznej jawnie:
sudo systemctl reload php8.2-fpmNa VPS z cPanel, ustawienia OPcache są często dostępne w interfejsie konfiguracji PHP, ale ręczne dostrajanie przez pliki .ini zawsze zapewnia dokładniejszą kontrolę.
Wzmacnianie bezpieczeństwa PHP-FPM
Uruchamiaj każdą pulę jako dedykowany użytkownik systemowy
Nigdy nie uruchamiaj pul PHP-FPM jako root ani jako współdzielony użytkownik www-data dla wielu aplikacji. Utwórz dedykowanego użytkownika systemowego dla każdej aplikacji:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myappNastępnie ustaw user = myapp i group = myapp w konfiguracji puli. Zapewnia to, że skompromitowana aplikacja PHP nie może odczytywać plików należących do innych aplikacji na tym samym serwerze.
Ogranicz funkcje PHP
W bloku php_admin_value puli wyłącz funkcje, które nie mają uzasadnionego zastosowania w aplikacjach WWW:
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_sourceOgranicz Open Basedir
Ogranicz dostęp PHP do plików do katalogu aplikacji:
php_admin_value[open_basedir] = /var/www/myapp:/tmpUżywaj gniazd Unix z rygorystycznymi uprawnieniami
Gniazda TCP (127.0.0.1:9000) są dostępne dla każdego procesu na serwerze. Gniazda Unix z listen.mode = 0660 ograniczają dostęp tylko do właściciela i grupy.
Weryfikacja pełnego stosu
Po skonfigurowaniu PHP-FPM i serwera WWW zweryfikuj cały łańcuch przed uruchomieniem produkcyjnym.
Przeładuj wszystkie usługi:
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginx
# or
sudo systemctl reload apache2Przetestuj składnię konfiguracji NGINX przed przeładowaniem:
sudo nginx -tUtwórz tymczasowy plik info (usuń go po weryfikacji — ujawnia wrażliwe dane serwera):
echo "<?php phpinfo();" | sudo tee /var/www/myapp/public/phpinfo.phpOtwórz http://example.com/phpinfo.php w przeglądarce i potwierdź:
- Server API pokazuje
FPM/FastCGI - PHP Version odpowiada zainstalowanej wersji
- Sekcja OPcache jest obecna i włączona
Następnie natychmiast usuń plik:
sudo rm /var/www/myapp/public/phpinfo.phpPHP-FPM w środowiskach wieloaplikacyjnych i wysokiego ruchu
Na Serwerze Dedykowanym hostującym dziesiątki aplikacji PHP, architektura wielopulowa staje się niezbędna. Każda aplikacja otrzymuje własną pulę z niezależnie dostrojonym pm.max_children, limitami pamięci i ścieżkami slow log. Nieprawidłowo działająca aplikacja, która wyczerpuje swoją pulę procesów roboczych, nie wpływa na inne aplikacje.
W scenariuszach wysokiego ruchu połącz PHP-FPM z:
- Buforowaniem FastCGI NGINX (
fastcgi_cache) do serwowania buforowanych odpowiedzi PHP jako pliki statyczne, całkowicie omijając PHP-FPM dla powtarzających się żądań - Redis lub Memcached do przechowywania sesji PHP, zastępując domyślne sesje oparte na plikach, które tworzą rywalizację I/O pod obciążeniem
- Skalowaniem poziomym przez uruchamianie PHP-FPM na serwerach aplikacji za load balancerem, z NGINX na oddzielnym węźle front-end
Jeśli Twój stos obejmuje terminację SSL, połączenie PHP-FPM z prawidłowo skonfigurowanymi Certyfikatami SSL na warstwie NGINX zapewnia, że uzgadnianie TLS jest obsługiwane zanim żądania dotrą do PHP-FPM, utrzymując procesy robocze PHP skupione wyłącznie na logice aplikacji.
W przypadku intensywnych obliczeniowo obciążeń PHP — wnioskowania uczenia maszynowego przez wiązania PHP, przetwarzania obrazów lub transkodowania wideo — rozważ Hosting GPU, gdzie PHP-FPM może delegować ciężkie obliczenia do bibliotek akcelerowanych GPU, zachowując standardową obsługę żądań dla warstwy WWW.
Kluczowa macierz decyzyjna i lista kontrolna techniczna
Przed wdrożeniem PHP-FPM w środowisku produkcyjnym zweryfikuj każdy element tej listy kontrolnej:
Wybór menedżera procesów
- Używaj
pm = dynamicdla ogólnych obciążeń VPS - Używaj
pm = statictylko na dedykowanych serwerach z przewidywalnym, stałym ruchem - Używaj
pm = ondemandtylko dla pul o niskim ruchu lub deweloperskich
Planowanie pojemności
- Zmierz rzeczywistą pamięć procesu roboczego za pomocą
psprzed ustawieniempm.max_children - Zarezerwuj co najmniej 20% całkowitej RAM dla systemu operacyjnego, serwera WWW i bazy danych
- Ustaw
pm.max_requestsmiędzy 500–1000, aby zapobiec akumulacji wycieków pamięci
Bezpieczeństwo
- Każda pula aplikacji działa jako własny użytkownik systemowy
clear_env = yesjest ustawione w każdej puliopen_basedirogranicza dostęp do plików do katalogu aplikacjidisable_functionsblokuje funkcje wykonywania powłoki- Używane są gniazda Unix zamiast gniazd TCP
Obserwowalność
pm.status_pathjest skonfigurowane i dostępne tylko z localhostslowlogjest włączone zrequest_slowlog_timeoutwynoszącym 2–5 sekund- Rotacja logów jest skonfigurowana dla wszystkich plików logów PHP-FPM
Wydajność
- OPcache jest włączone z
validate_timestamps=0w środowisku produkcyjnym - Buforowanie FastCGI NGINX jest skonfigurowane dla buforowalnych punktów końcowych
- Obsługa sesji PHP jest ustawiona na Redis lub Memcached, nie pliki
Operacyjne
sudo systemctl reload php8.2-fpmjest używane do zmian konfiguracji bez przestojów (nierestart)phpinfo.phpjest usuwane z katalogu głównego dokumentów natychmiast po weryfikacji- Konfiguracja puli jest kontrolowana wersją wraz z kodem aplikacji
FAQ
Jaka jest różnica między PHP-FPM a mod_php?
mod_php osadza interpreter PHP wewnątrz każdego procesu roboczego Apache, zużywając pamięć nawet podczas serwowania plików statycznych i ściśle łącząc PHP z Apache. PHP-FPM działa jako całkowicie oddzielna usługa, komunikuje się przez FastCGI, działa z dowolnym serwerem WWW, w tym NGINX, i umożliwia izolację procesów per-aplikacja z niezależnymi limitami zasobów.
Jak wybrać między gniazdem Unix a gniazdem TCP dla PHP-FPM?
Używaj gniazda Unix (listen = /run/php/app-fpm.sock) zawsze, gdy PHP-FPM i serwer WWW działają na tej samej fizycznej lub wirtualnej maszynie. Gniazda Unix omijają stos TCP/IP, zmniejszając opóźnienia i eliminując konflikty portów. Używaj gniazda TCP (listen = 127.0.0.1:9000) tylko wtedy, gdy PHP-FPM działa na innym hoście niż serwer WWW.
Dlaczego otrzymuję błędy 502 Bad Gateway pod obciążeniem?
Błąd 502 z NGINX wskazujący na PHP-FPM prawie zawsze oznacza, że kolejka nasłuchiwania jest pełna — wszystkie procesy robocze są zajęte i nowe połączenia są odrzucane. Sprawdź pm.status_path pod kątem niezerowej wartości listen queue. Rozwiązaniem jest albo zwiększenie pm.max_children (jeśli RAM pozwala) albo optymalizacja wolnych skryptów PHP zidentyfikowanych przez slow log.
Jak przeładować PHP-FPM bez przerywania aktywnych połączeń?
Używaj sudo systemctl reload php8.2-fpm zamiast restart. Sygnał reload (SIGUSR2) powoduje, że proces główny płynnie restartuje procesy robocze: istniejące żądania kończą się normalnie, podczas gdy nowe procesy robocze przejmują zaktualizowaną konfigurację. Twardy restart natychmiast kończy wszystkie procesy robocze, przerywając żądania w trakcie realizacji.
Czy PHP-FPM może jednocześnie obsługiwać wiele wersji PHP na jednym serwerze?
Tak. Zainstaluj wiele wersji PHP (np. php7.4-fpm i php8.2-fpm) i skonfiguruj każdą pulę aplikacji tak, aby używała odpowiedniej ścieżki gniazda. W NGINX wskaż fastcgi_pass na właściwe gniazdo dla każdego bloku serwera. Jest to standardowy wzorzec na współdzielonej infrastrukturze zarządzanej przez Panele Sterowania VPS i jest w pełni obsługiwany na Hostingu VPS z dostępem root.
