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
| File | Includes SCRIPT_FILENAME? | Use When |
|---|---|---|
fastcgi.conf | ✅ Yes | Standard — use this |
fastcgi_params | ❌ No | Only 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
| Problem | Symptom | Fix |
|---|---|---|
| Socket doesn't exist | 502 Bad Gateway | Check php-fpm is running and socket path |
| Socket permission denied | 502 + "connect() to socket failed: Permission denied" | chown www-data:www-data on socket dir |
| Wrong socket path | 502 | grep "listen " /etc/php/8.4/fpm/pool.d/www.conf |
| PHP file downloads instead of running | Raw PHP in browser | Add location ~ \.php$ block to nginx config |
| 404 on PHP files | File not found | Verify root directive and file path |
No try_files $uri =404 | Remote code injection risk | Always 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
| Pitfall | What happens | Fix |
|---|---|---|
| Editing config without reloading | Changes not applied | sudo nginx -s reload after every edit |
Not running nginx -t first | Reload breaks with syntax error | Always test syntax before reloading |
| Wrong socket path for PHP-FPM | 502 Bad Gateway | ls /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.