Skip to main content

PHP-FPM Installation and Connection

Learning Focus

Leave this lesson with a working understanding of php-fpm installation and connection that you can apply immediately in production.

Nginx does not execute PHP internally. It delegates PHP requests to PHP-FPM (FastCGI Process Manager) via a socket. This page covers everything from installation to a verified working PHP setup.


Install PHP-FPM

Ubuntu / Debian

# Add Ondrej Sury's PHP PPA for newer PHP versions
sudo apt install -y lsb-release apt-transport-https ca-certificates
sudo curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg \
https://packages.sury.org/php/apt.gpg

echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] \
https://packages.sury.org/php/ $(lsb_release -sc) main" \
| sudo tee /etc/apt/sources.list.d/php.list

sudo apt update

# Install PHP 8.4 and FPM
sudo apt install -y php8.4-fpm php8.4-common php8.4-mysql php8.4-curl \
php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip php8.4-opcache

# Enable and start
sudo systemctl enable --now php8.4-fpm
sudo systemctl status php8.4-fpm

AlmaLinux / Rocky Linux / RHEL

# Enable Remi repository
sudo dnf install -y epel-release
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module enable php:remi-8.4

# Install PHP-FPM
sudo dnf install -y php php-fpm php-common php-mysqlnd php-curl \
php-gd php-mbstring php-xml php-zip php-opcache

# Start and enable
sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm

Verify PHP-FPM Socket

# Debian/Ubuntu — socket per version
ls -la /run/php/
# Expected: php8.4-fpm.sock

# RHEL/AlmaLinux
ls -la /run/php-fpm/
# Expected: www.sock

# Check the socket path in FPM config
grep "listen " /etc/php/8.4/fpm/pool.d/www.conf | head -3
# or
grep "listen " /etc/php-fpm.d/www.conf | head -3

Connect Nginx to PHP-FPM

The key configuration is in the location ~ \.php$ block in your server block.

/etc/nginx/conf.d/example.com.conf
server {
listen 443 ssl http2;
server_name example.com;

root /var/www/example.com/public;
index index.php index.html;

ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;

# Main routing — try static file, then directory, then PHP
location / {
try_files $uri $uri/ /index.php?$args;
}

# PHP-FPM handler
location ~ \.php$ {
# Security: ensure the script exists before passing to FPM
try_files $uri =404;

# Split path info (needed for some frameworks)
fastcgi_split_path_info ^(.+\.php)(/.+)$;

# Socket path (Debian/Ubuntu)
fastcgi_pass unix:/run/php/php8.4-fpm.sock;

# Set FastCGI parameters
include fastcgi.conf;

# Pass PHP-specific variables
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# Block PHP in uploads
location ~* /uploads/.*\.php$ {
deny all;
}
}
UNIX Socket vs TCP
  • UNIX socket (unix:/run/php/php8.4-fpm.sock) — faster, same host only
  • TCP (127.0.0.1:9000) — slightly slower, but works if FPM is on a different host

Always prefer UNIX socket on single-server setups.


fastcgi.conf vs fastcgi_params

FileIncludes SCRIPT_FILENAME?Use When
fastcgi.conf✅ YesStandard — use this
fastcgi_params❌ NoOnly if adding SCRIPT_FILENAME manually
# Using fastcgi.conf (includes SCRIPT_FILENAME automatically)
include fastcgi.conf;

# Using fastcgi_params (must add SCRIPT_FILENAME yourself)
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Test PHP is Executing

# Create a PHP info file (TEMPORARY — delete after testing!)
echo "<?php phpinfo();" | sudo tee /var/www/example.com/public/info.php

# Test from CLI
curl -s http://localhost/info.php -H "Host: example.com" | grep -i "PHP Version"

# Delete the info file
sudo rm /var/www/example.com/public/info.php

Expected: PHP Version 8.4.x in the output.

If you get the raw PHP code downloaded instead — the location ~ \.php$ block is missing or wrong.


PHP-FPM Process Management

# Status
sudo systemctl status php8.4-fpm

# Restart (needed after php.ini changes)
sudo systemctl restart php8.4-fpm

# Reload (graceful — for pool config changes)
sudo systemctl reload php8.4-fpm

# Check how many PHP workers are running
ps aux | grep php-fpm | grep -v grep | wc -l

# View active PHP-FPM processes
ps aux | grep php-fpm | grep -v grep

Common PHP-FPM Connection Issues

ProblemSymptomFix
Socket doesn't exist502 Bad GatewayCheck php-fpm is running and socket path
Socket permission denied502 + "connect() to socket failed: Permission denied"chown www-data:www-data on socket dir
Wrong socket path502grep "listen " /etc/php/8.4/fpm/pool.d/www.conf
PHP file downloads instead of runningRaw PHP in browserAdd location ~ \.php$ block to nginx config
404 on PHP filesFile not foundVerify root directive and file path
No try_files $uri =404Remote code injection riskAlways add it before fastcgi_pass

Hands-On Practice

# Verify Nginx is running
sudo systemctl status nginx

# Test config syntax
sudo nginx -t

# Reload without downtime
sudo nginx -s reload

# Check error log
sudo tail -20 /var/log/nginx/error.log

Common Pitfalls

PitfallWhat happensFix
Editing config without reloadingChanges not appliedsudo nginx -s reload after every edit
Not running nginx -t firstReload breaks with syntax errorAlways test syntax before reloading
Wrong socket path for PHP-FPM502 Bad Gatewayls /run/php/ and verify the exact socket filename

What's Next

  • Continue to the next lesson in this module, or go to the module index for an overview.
  • Use the Cheatsheets for quick CLI reference.