Configure PHP-FPM with optimized memory pools, process management, and connection limits to handle high-traffic websites efficiently. Learn production-grade tuning strategies for maximum performance.
Prerequisites
- Root or sudo access
- Basic knowledge of web server concepts
- 1GB+ available RAM
What this solves
PHP-FPM (FastCGI Process Manager) is the recommended way to run PHP applications in production, but default configurations often perform poorly under high traffic. This tutorial shows you how to optimize PHP-FPM memory usage, configure process pools effectively, and implement connection limits to prevent server overload while maintaining fast response times.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest PHP-FPM version with security patches.
sudo apt update && sudo apt upgrade -y
Install PHP-FPM and essential extensions
Install PHP-FPM with commonly needed extensions for web applications. This includes OpCache for bytecode caching and essential database drivers.
sudo apt install -y php8.3-fpm php8.3-opcache php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip
Configure PHP memory limits and execution settings
Optimize PHP's core memory settings in php.ini. These settings prevent memory exhaustion while allowing sufficient resources for complex applications.
memory_limit = 256M
max_execution_time = 60
max_input_time = 60
post_max_size = 64M
upload_max_filesize = 32M
max_file_uploads = 20
date.timezone = UTC
Enable and optimize OpCache for bytecode caching
OpCache dramatically improves PHP performance by storing precompiled script bytecode in memory, eliminating the need to load and parse PHP files on each request.
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.enable_file_override=1
Configure PHP-FPM process pool settings
The process manager controls how PHP-FPM spawns and manages worker processes. Dynamic mode provides the best balance of performance and resource efficiency for high-traffic sites.
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 1000
Configure connection limits and timeouts
Set appropriate timeouts and connection limits to prevent hanging processes and ensure responsive behavior under load. These settings help PHP-FPM handle traffic spikes gracefully.
request_terminate_timeout = 120
rlimit_files = 4096
rlimit_core = 0
catch_workers_output = yes
decorate_workers_output = no
clear_env = no
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
Configure PHP-FPM global settings
Set global PHP-FPM parameters that control logging, emergency restart behavior, and process limits. These settings ensure stability under high load conditions.
pid = /run/php/php8.3-fpm.pid
error_log = /var/log/php8.3-fpm.log
log_level = warning
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
daemonize = yes
rlimit_files = 4096
rlimit_core = 0
events.mechanism = epoll
Create PHP-FPM status monitoring endpoint
Enable the PHP-FPM status page to monitor pool performance, active connections, and memory usage. This is essential for troubleshooting performance issues.
pm.status_path = /php-fpm-status
ping.path = /php-fpm-ping
ping.response = pong
pm.status_listen = 127.0.0.1:9001
Optimize system limits for PHP-FPM
Configure system-level limits to ensure PHP-FPM can handle the configured number of processes and file descriptors without hitting OS limits.
www-data soft nofile 4096
www-data hard nofile 8192
www-data soft nproc 2048
www-data hard nproc 4096
For RHEL-based systems use 'nginx' instead of 'www-data'
nginx soft nofile 4096
nginx hard nofile 8192
nginx soft nproc 2048
nginx hard nproc 4096
Configure systemd service limits
Create systemd overrides to ensure PHP-FPM can use the configured resource limits. This prevents systemd from restricting PHP-FPM processes.
sudo mkdir -p /etc/systemd/system/php8.3-fpm.service.d
[Service]
LimitNOFILE=8192
LimitNPROC=4096
TasksMax=4096
Enable and start PHP-FPM service
Reload systemd configuration and start PHP-FPM with the optimized settings. Enable the service to start automatically on system boot.
sudo systemctl daemon-reload
sudo systemctl enable --now php8.3-fpm
sudo systemctl status php8.3-fpm
Verify your setup
Check that PHP-FPM is running correctly and verify the optimized configuration is active.
sudo systemctl status php8.3-fpm
sudo php-fpm8.3 -t
php -m | grep -i opcache
curl -s http://127.0.0.1:9001/php-fpm-status
Check PHP-FPM process information and memory usage:
ps aux | grep php-fpm
sudo ss -tuln | grep 9001
free -h
Performance monitoring and troubleshooting
Monitor PHP-FPM performance using the status endpoint and system tools. You can integrate this with Netdata for real-time monitoring or htop for process analysis.
# Monitor pool status
curl -s http://127.0.0.1:9001/php-fpm-status?full
Watch process spawning
watch -n 2 'ps aux | grep php-fpm | wc -l'
Monitor slow requests
tail -f /var/log/php*fpm.log
Check OpCache status
php -r "print_r(opcache_get_status());"
Monitor memory usage per process
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | grep php-fpm
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| 502 Bad Gateway errors | Socket permission issues | Check listen.owner/group matches web server user |
| Slow response times | Too few PHP-FPM processes | Increase pm.max_children and pm.start_servers |
| High memory usage | Memory leaks or too many processes | Set pm.max_requests lower (500-1000) to restart workers |
| Connection timeouts | Long-running scripts | Increase request_terminate_timeout and max_execution_time |
| PHP-FPM won't start | Configuration syntax errors | Run sudo php-fpm -t to test configuration |
| OpCache not working | Extension not loaded | Verify opcache.enable=1 in correct php.ini file |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Global variables
PHP_VERSION=""
PHP_INI_PATH=""
PHP_FPM_POOL_PATH=""
PHP_FPM_SERVICE=""
WEB_USER=""
WEB_GROUP=""
SOCKET_PATH=""
# Cleanup function
cleanup() {
echo -e "${RED}Installation failed. Check logs above for details.${NC}"
exit 1
}
trap cleanup ERR
# Check if running as root or with sudo
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root or with sudo${NC}"
exit 1
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
PHP_VERSION="8.3"
PHP_INI_PATH="/etc/php/8.3/fpm/php.ini"
PHP_FPM_POOL_PATH="/etc/php/8.3/fpm/pool.d/www.conf"
PHP_FPM_SERVICE="php8.3-fpm"
WEB_USER="www-data"
WEB_GROUP="www-data"
SOCKET_PATH="/run/php/php8.3-fpm.sock"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
PHP_VERSION=""
PHP_INI_PATH="/etc/php.ini"
PHP_FPM_POOL_PATH="/etc/php-fpm.d/www.conf"
PHP_FPM_SERVICE="php-fpm"
WEB_USER="nginx"
WEB_GROUP="nginx"
SOCKET_PATH="/run/php-fpm/www.sock"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
PHP_VERSION=""
PHP_INI_PATH="/etc/php.ini"
PHP_FPM_POOL_PATH="/etc/php-fpm.d/www.conf"
PHP_FPM_SERVICE="php-fpm"
WEB_USER="nginx"
WEB_GROUP="nginx"
SOCKET_PATH="/run/php-fpm/www.sock"
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect OS distribution${NC}"
exit 1
fi
echo -e "${GREEN}Starting PHP-FPM optimization for $PRETTY_NAME${NC}"
# Step 1: Update system packages
echo -e "${GREEN}[1/7] Updating system packages...${NC}"
$PKG_UPDATE
# Step 2: Install PHP-FPM and extensions
echo -e "${GREEN}[2/7] Installing PHP-FPM and extensions...${NC}"
case "$ID" in
ubuntu|debian)
$PKG_INSTALL php${PHP_VERSION}-fpm php${PHP_VERSION}-opcache php${PHP_VERSION}-mysql php${PHP_VERSION}-curl php${PHP_VERSION}-gd php${PHP_VERSION}-mbstring php${PHP_VERSION}-xml php${PHP_VERSION}-zip
;;
*)
$PKG_INSTALL php-fpm php-opcache php-mysqlnd php-curl php-gd php-mbstring php-xml php-zip
;;
esac
# Step 3: Configure PHP memory limits and execution settings
echo -e "${GREEN}[3/7] Configuring PHP memory limits and execution settings...${NC}"
cp "$PHP_INI_PATH" "$PHP_INI_PATH.backup"
cat >> "$PHP_INI_PATH" << 'EOF'
; Optimized memory and execution settings
memory_limit = 256M
max_execution_time = 60
max_input_time = 60
post_max_size = 64M
upload_max_filesize = 32M
max_file_uploads = 20
date.timezone = UTC
EOF
# Step 4: Enable and optimize OpCache
echo -e "${GREEN}[4/7] Configuring OpCache for bytecode caching...${NC}"
cat >> "$PHP_INI_PATH" << 'EOF'
; OpCache optimization settings
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.enable_file_override=1
EOF
# Step 5: Configure PHP-FPM process pool settings
echo -e "${GREEN}[5/7] Configuring PHP-FPM process pool settings...${NC}"
cp "$PHP_FPM_POOL_PATH" "$PHP_FPM_POOL_PATH.backup"
cat > "$PHP_FPM_POOL_PATH" << EOF
[www]
user = $WEB_USER
group = $WEB_GROUP
listen = $SOCKET_PATH
listen.owner = $WEB_USER
listen.group = $WEB_GROUP
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 1000
request_terminate_timeout = 120
rlimit_files = 4096
rlimit_core = 0
catch_workers_output = yes
decorate_workers_output = no
clear_env = no
env[HOSTNAME] = \$HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
EOF
# Step 6: Configure global PHP-FPM settings
echo -e "${GREEN}[6/7] Configuring global PHP-FPM settings...${NC}"
case "$ID" in
ubuntu|debian)
FPM_CONF="/etc/php/8.3/fpm/php-fpm.conf"
;;
*)
FPM_CONF="/etc/php-fpm.conf"
;;
esac
if [ -f "$FPM_CONF" ]; then
cp "$FPM_CONF" "$FPM_CONF.backup"
# Set global parameters
sed -i 's/^;emergency_restart_threshold.*/emergency_restart_threshold = 10/' "$FPM_CONF"
sed -i 's/^;emergency_restart_interval.*/emergency_restart_interval = 1m/' "$FPM_CONF"
sed -i 's/^;process_control_timeout.*/process_control_timeout = 10s/' "$FPM_CONF"
fi
# Create necessary directories and set permissions
mkdir -p $(dirname "$SOCKET_PATH")
chown -R $WEB_USER:$WEB_GROUP $(dirname "$SOCKET_PATH")
# Step 7: Start and enable PHP-FPM service
echo -e "${GREEN}[7/7] Starting and enabling PHP-FPM service...${NC}"
systemctl enable $PHP_FPM_SERVICE
systemctl restart $PHP_FPM_SERVICE
# Verification checks
echo -e "${GREEN}Performing verification checks...${NC}"
# Check if PHP-FPM is running
if systemctl is-active --quiet $PHP_FPM_SERVICE; then
echo -e "${GREEN}✓ PHP-FPM service is running${NC}"
else
echo -e "${RED}✗ PHP-FPM service failed to start${NC}"
systemctl status $PHP_FPM_SERVICE
exit 1
fi
# Check if socket exists
if [ -S "$SOCKET_PATH" ]; then
echo -e "${GREEN}✓ PHP-FPM socket created successfully${NC}"
else
echo -e "${RED}✗ PHP-FPM socket not found${NC}"
exit 1
fi
# Check PHP configuration
php --version > /dev/null 2>&1 && echo -e "${GREEN}✓ PHP is working correctly${NC}" || echo -e "${RED}✗ PHP configuration error${NC}"
# Check OpCache status
php -m | grep -q "Zend OPcache" && echo -e "${GREEN}✓ OpCache is enabled${NC}" || echo -e "${YELLOW}⚠ OpCache not detected${NC}"
echo -e "${GREEN}PHP-FPM optimization completed successfully!${NC}"
echo -e "${YELLOW}Configuration summary:${NC}"
echo "- PHP-FPM service: $PHP_FPM_SERVICE"
echo "- Socket path: $SOCKET_PATH"
echo "- Process pool: Dynamic (5-50 workers)"
echo "- Memory limit: 256M per process"
echo "- OpCache: 256M allocated"
echo "- Max requests per worker: 1000"
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Configure your web server (Nginx/Apache) to use PHP-FPM"
echo "2. Monitor performance with: systemctl status $PHP_FPM_SERVICE"
echo "3. Adjust pm.max_children based on your server's RAM"
echo "4. Review logs: journalctl -u $PHP_FPM_SERVICE"
Review the script before running. Execute with: bash install.sh