Image

Knowledge base → Setting up php-fpm on VPS for high traffic sites

[Virtual servers] [Applications on VPS/VDS]
Date of publication: 04.06.2025

Websites with high traffic require special service configuration. Depending on the chosen service, there are recommendations for achieving high VPS server performance.

Simply increasing resources is not enough to achieve high VPS server performance—you also need to configure the services.

By default, services have basic settings suitable for typical projects with low to medium traffic.

For clarity, we’ll define traffic criteria:

  • Low website traffic: up to 10,000 visits per month (~300 per day, 1 visit every 5 minutes).
  • Medium website traffic: 10,000 to 90,000 visits—let’s take an average of 50,000 per month (~1,600 per day, 1 visit every minute).
  • High website traffic: 100,000 visits per month and above, i.e., from 2 visits per minute and higher.

Let’s examine a configuration where nginx acts as the proxy web server and php-fpm (in this case, php8.3-fpm) as the main handler.

We used a VPS server running Linux Debian 12, with 6 CPU cores, 8 GB RAM, and a 50 GB SSD.

We’ll optimize performance specifically for php8.3-fpm. With the default configuration, we ran a link analysis script, and within a few minutes, the request rate dropped from 9 requests per second to 5. After another 5 minutes, performance fell to 1.5 requests per second, and the service began to choke, soon returning a Bad Gateway error.

Meanwhile, server load didn’t increase significantly, and there were still enough resources to handle requests.

1. New php-fpm Configuration File

Create a new configuration file in /etc/php/8.3/fpm/pool.d/domain.conf.

Rename the old file to www.conf.bak —this ensures it won’t be used, but you can revert to it by removing the .bak suffix if needed.

2. Configuration Details

[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Process manager
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500
pm.max_spawn_rate = 32
pm.process_idle_timeout = 10s

; Connection settings
listen.backlog = 65535
listen.allowed_clients = 127.0.0.1

; Timeouts
request_terminate_timeout = 30s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm/slow.log

; Status monitoring
pm.status_path = /fpm-status
ping.path = /ping
ping.response = pong

; Logging
access.log = /var/log/php8.3-fpm/access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}dms"
catch_workers_output = yes
decorate_workers_output = no

; Security
security.limit_extensions = .php .php3 .php4 .php5 .php7 .php8
clear_env = no

; Performance tweaks
rlimit_files = 65535
rlimit_core = unlimited

; PHP settings
php_admin_value[error_log] = /var/log/php8.3-fpm/error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 128M
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 32M
php_admin_value[max_execution_time] = 30
php_admin_value[session.gc_maxlifetime] = 1440

3. Additional Settings

If the log directory doesn’t exist, create it:

mkdir -p /var/log/php8.3-fpm

3.1 Configuration Comments

  • listen = 127.0.0.1:9000 - Must match the nginx configuration or vice versa.
  • pm.max_children = 50 - Depends on available RAM (50 MB per child → ~2 GB for PHP).
  • pm.start_servers = 10 - ~20% of pm.max_children.
  • pm.max_requests = 500 - Restarts workers after 500 requests to prevent memory leaks (unnoticeable to visitors and doesn’t affect operation).
  • request_terminate_timeout = 30s - Terminates hung requests.
  • request_slowlog_timeout = 5s - Logs slow requests.
  • slowlog = /var/log/php8.3-fpm/slow.log

The slow request log may not be very useful for CMS-based sites, as it will show a call stack, but it can provide helpful timing information for request start and completion.

To monitor /fpm-status, add the following to the nginx server block:

    location = /fpm-status {
        allow 127.0.0.1;    # Allow only local access
        allow 192.168.10.100; # Or your trusted network
        deny all;          # Deny all others

        include /etc/nginx/fastcgi_params;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $request_filename;
    }

The output will look like this:

pool:                 www
process manager:      dynamic
start time:           04/Jun/2025:18:16:50 +0000
start since:          814
accepted conn:        239
listen queue:         0
max listen queue:     0
listen queue len:     128
idle processes:       9
active processes:     1
total processes:      10
max active processes: 3
max children reached: 0
slow requests:        0

4. Apply the New Configuration

Check the configuration file for errors:

php-fpm8.3 -t

If no errors are found, restart the service:

systemctl restart php8.3-fpm

5. Retesting

With the new configuration, CPU load increased noticeably during similar testing, but performance only dropped by 20% (from 9 requests per second to 7) after a few minutes and stabilized. All requests (~10,000 over 25 minutes, equating to 400 requests per minute or ~7 per second) were successfully processed with a 200 status code.

Visually, navigating the site during simultaneous testing showed noticeably faster performance. With this web service configuration, server resources are now fully utilized rather than idling as before.





No Comments Yet