Servery a DevOps

Nginx + PHP-FPM na Ubuntu 24.04: konfigurace a ladění výkonu

Instalace PHP 8.3-FPM s nginx, správný server blok, bezpečné location pro PHP, nastavení pm.max_children podle RAM, OPcache a řešení chyb 502 a 504.

12. 10. 2026 · 4 min čtení

Nginx PHP sám nespouští. Požadavky na .php předává přes protokol FastCGI službě PHP-FPM, která drží pool PHP procesů. Ukážeme kompletní nastavení na Ubuntu 24.04 (výchozí PHP 8.3) včetně výpočtu počtu workerů.

Instalace

sudo apt update
sudo apt install nginx php8.3-fpm php8.3-mysql php8.3-curl php8.3-mbstring php8.3-xml php8.3-zip php8.3-intl php8.3-gd
systemctl status php8.3-fpm

PHP-FPM naslouchá na unix socketu /run/php/php8.3-fpm.sock. Socket je rychlejší než TCP a není dostupný ze sítě.

Server blok

/etc/nginx/sites-available/mujweb.cz:

server {
    listen 80;
    listen [::]:80;
    server_name mujweb.cz www.mujweb.cz;

    root /var/www/mujweb/public;
    index index.php index.html;

    client_max_body_size 32m;

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

    location ~ \.php$ {
        try_files $uri =404;
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    # skryté soubory (.env, .git) nikdy nevydávat
    location ~ /\.(?!well-known) {
        deny all;
    }

    location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|woff2)$ {
        expires 30d;
        access_log off;
    }
}
sudo ln -s /etc/nginx/sites-available/mujweb.cz /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Snippet fastcgi-php.conf je součástí balíčku nginx v Ubuntu a nastavuje SCRIPT_FILENAME a PATH_INFO. Řádek try_files $uri =404; brání tomu, aby PHP spustilo jiný soubor, než který existuje (např. nahraný obrázek s PHP kódem uvnitř při požadavku /upload/foto.jpg/x.php).

Rychlý test:

echo '<?php echo PHP_VERSION;' | sudo tee /var/www/mujweb/public/test.php
curl http://mujweb.cz/test.php
sudo rm /var/www/mujweb/public/test.php

Nikdy nenechávejte na produkci soubor s phpinfo().

Pool PHP-FPM

Výchozí pool je v /etc/php/8.3/fpm/pool.d/www.conf. Klíčové volby:

user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock

pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
Volba Význam
pm = dynamic počet procesů se mění podle zátěže
pm = ondemand procesy vznikají až při požadavku, šetří RAM na málo navštěvovaných webech
pm = static pevný počet procesů, předvídatelné pro vytížené servery
pm.max_children maximum současně zpracovaných PHP požadavků
pm.max_requests proces se po N požadavcích recykluje (proti únikům paměti)

Jak spočítat pm.max_children

Zjistěte průměrnou paměť jednoho PHP-FPM procesu pod zátěží:

ps -C php-fpm8.3 -o rss= | awk '{s+=$1; n++} END {print s/n/1024 " MB průměr, " n " procesů"}'

Vzorec:

max_children = (RAM pro PHP) / (průměrná velikost procesu)

Příklad: VPS 2 GB, systém + MariaDB + nginx potřebuje ~900 MB, zbývá ~1 100 MB. Proces má průměrně 60 MB → 1100 / 60 ≈ 18. Nastavte s rezervou pm.max_children = 15.

Příliš vysoká hodnota vede ke swapování a pádům, příliš nízká k čekání ve frontě. V logu poznáte druhý případ:

sudo grep 'max_children' /var/log/php8.3-fpm.log
WARNING: [pool www] server reached pm.max_children setting (10), consider raising it

Po úpravě:

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

OPcache

OPcache ukládá zkompilovaný PHP kód do paměti a je zapnutá ve výchozím stavu. Pro produkci upravte /etc/php/8.3/fpm/conf.d/10-opcache.ini:

opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60

S validate_timestamps=0 je výkon ještě o něco vyšší, ale po každém nasazení pak musíte PHP-FPM reloadnout, jinak poběží starý kód.

Limity uploadu a běhu

Upravte v /etc/php/8.3/fpm/php.ini (ne v cli/php.ini – ten platí jen pro příkazovou řádku):

upload_max_filesize = 32M
post_max_size = 32M
memory_limit = 256M
max_execution_time = 60

client_max_body_size v nginx musí být alespoň stejně velký, jinak nginx vrátí 413 Request Entity Too Large dřív, než se požadavek dostane k PHP.

Ladění chyb

502 Bad Gateway – nginx se nedostal k PHP-FPM:

sudo tail -n 20 /var/log/nginx/error.log
systemctl status php8.3-fpm
ls -l /run/php/

Typicky connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory) – v konfiguraci je stará verze PHP po upgradu systému.

504 Gateway Timeout – skript běží déle než fastcgi_read_timeout (výchozí 60 s). Najděte pomalý kód přes slowlog v poolu:

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

Prázdná stránka bez chyby – zkontrolujte, že SCRIPT_FILENAME odpovídá root (řeší ho snippet) a že PHP soubor je čitelný pro www-data.

Více webů, oddělené pooly

Pro každý web můžete vytvořit vlastní pool s vlastním uživatelem a socketem (/etc/php/8.3/fpm/pool.d/mujweb.conf, listen = /run/php/mujweb.sock). Kompromitace jednoho webu pak nedává přístup k souborům ostatních – užitečné, pokud na serveru hostujete projekty více klientů.