Linux 04.07.2026 2 min read by Ulrich Emmerich

Set up Nginx and PHP 8.4-FPM on a LAN server

Install Nginx and PHP 8.4-FPM from Debian 13's official repositories and configure safe PHP routing, upload limits, and optional TLS.

Nginx PHP PHP-FPM Debian LAN-Server
Set up Nginx and PHP 8.4-FPM on a LAN server

Nginx and PHP 8.4 without a third-party repository

Debian 13 “Trixie” contains PHP 8.4 in its official repositories. A new installation in August 2026 therefore does not need the Sury repository. This keeps updates inside Debian's package and security model.

1. Install packages


sudo apt update
sudo apt install -y nginx php8.4-fpm php8.4-cli php8.4-curl \
  php8.4-xml php8.4-mbstring php8.4-zip php8.4-gd php8.4-opcache
sudo systemctl enable --now nginx php8.4-fpm

Install php8.4-mysql only if the application uses MySQL or MariaDB.

2. Create the document root


sudo install -d -o www-data -g www-data -m 0750 /var/www/example/public

Use a short test file and remove it after validation:


<?php
header('Content-Type: text/plain; charset=utf-8');
echo "PHP works\n";

Do not leave a public phpinfo() page behind; it exposes extensive runtime information.

3. Configure the Nginx server block


server {
    listen 80;
    server_name server.example.lan;
    root /var/www/example/public;
    index index.php index.html;
    client_max_body_size 20m;

    location / {
        try_files $uri $uri/ =404;
    }

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

    location ~ /\. { deny all; }
}

sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
sudo nginx -t
sudo systemctl reload nginx

Nginx documents client_max_body_size for http, server, and location; its default is 1m. The example scopes it to this server.

4. Align PHP limits

In /etc/php/8.4/fpm/php.ini:


upload_max_filesize = 20M
post_max_size = 22M

post_max_size must not be smaller than the file limit. Restart FPM after the change and account for any application-specific limit.

5. Size PHP-FPM from measurements

pm.max_children limits concurrent PHP requests. Do not calculate it from total RAM alone; the OS, database, and other services need memory too.


pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 4
pm.max_requests = 500

These are conservative starting values, not a benchmark. Measure real worker memory and inspect FPM logs before raising them.

6. Choose TLS for the actual network

Let's Encrypt can issue publicly trusted certificates when the domain and ACME challenge can be validated. A private name such as server.example.lan cannot use the normal public HTTP challenge. Use an internal CA, another appropriate internal TLS design, or keep access inside a trusted LAN/VPN.

For a public domain that meets the requirements:


sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Public exposure materially increases the attack surface and is outside a LAN-only design.

Troubleshooting

  • Run nginx -t before every reload.
  • Inspect systemctl status nginx php8.4-fpm and the journal.
  • For 502 Bad Gateway, check the FPM service and socket path.
  • For HTTP 413, compare Nginx, PHP, and application limits.
  • php -v checks the CLI; it does not prove which FPM socket Nginx uses.

Sources and technical documentation

Links marked with * are affiliate links. As an Amazon Associate I earn from qualifying purchases.

More articles about Linux