Install PHP-FPM for a website

2 min read07 November 2026

Nginx cannot execute PHP: it forwards requests to a dedicated service, PHP-FPM. This separation lets you restart PHP without stopping Nginx, limit resources per pool and avoid running files with the web server's privileges. Here is PHP 8.4-FPM on Debian 13, the socket configuration and the matching Nginx block.

Install PHP 8.4

Bash
sudo apt update
sudo apt install -y php8.4-fpm php8.4-cli php8.4-mysql
systemctl status php8.4-fpm
ls -l /run/php/

The package creates the /run/php/php8.4-fpm.sock socket and the php8.4-fpm service. Install only the extensions you need (php8.4-curl, php8.4-mbstring, php8.4-xml, php8.4-gd), then reload the service. php -v on the command line uses the CLI client, which may not share the same configuration as FPM.

The pool and the socket

/etc/php/8.4/fpm/pool.d/www.conf sets user = www-data, group = www-data and listen = /run/php/php8.4-fpm.sock. A Unix socket avoids exposing PHP on a TCP port. listen.owner, listen.group and listen.mode = 0660 must let Nginx, which also runs as www-data, access it.

Bash
sudo php-fpm8.4 -t
sudo systemctl reload php8.4-fpm

php-fpm8.4 -t checks the configuration syntax. If you change listen, update the Nginx block immediately: a missing socket produces a 502.

Nginx to PHP-FPM

Nginx
server {
    listen 80;
    listen [::]:80;
    server_name your-domain.com;

    root /var/www/site;
    index index.php index.html;

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

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

snippets/fastcgi-php.conf includes try_files and computes SCRIPT_FILENAME. Do not replace it with an incomplete version. Without try_files, a nonexistent PHP URL can be passed to FPM. Validate and reload:

Bash
sudo nginx -t
sudo systemctl reload nginx

Permissions

Files belong to the deployment user, with the www-data group. Directories are 750, files are 640. PHP only needs to read the code. An uploads directory must be writable by www-data, but never executable.

Bash
sudo chown -R deploy:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 750 {} \;
sudo find /var/www/site -type f -exec chmod 640 {} \;

777 permissions are not a solution: they open the code to every account on the server.

Test and troubleshoot

Create info.php with <?php phpinfo();, open it, then delete it immediately. A 502 means a missing socket, wrong path or permission issue; 403, index or rights; 404, root or try_files. Check /var/log/nginx/error.log, /var/log/php8.4-fpm.log and journalctl -u php8.4-fpm. Verify that www-data can traverse every parent directory with namei -l /var/www/site/index.php.

Advertisement

Was this article helpful?

Read next