Tuning PHP FPM on Nginx

by | Jun 25, 2023 | Technology | 0 comments

As the Store Locator Plus® SaaS platform grows, we find it more important than ever to fine-tune our web server and PHP processes to serve the more critical data and network demands better. A recent performance review showed process timeouts during large data imports and side-loading, especially when the read and write endpoints hit the same server node. Here are some things we did to improve performance.

1080 words, 4 min 19 sec read

Get off faux sockets.

PHP FPM is typically installed with file-based sockets. While this lessens the traffic on the network hardware, most modern servers are equipped with fiber-ready network connections. These network ports and the TCP stack that interfaces with them can often handle a higher peak load of I/O requests than the file system can manage via the “pretend” sockets run through the operating system file I/O requests.

If your server runs on solid-state drives, there may be less disparity between the I/O rate of a network stack and a file stack. We use high-end SSD drives and high-speed network cards on the servers — yet we still found we could push through nearly twice as many requests to PHP-FPM from nginx when switching to TCP sockets.

Configure PHP FPM for TCP sockets

You’ll need to configure your PHP FPM to listen on 127.0.0.1:9000 — the network loopback address port 9000. It is a good idea to set the only client to be yourself as well.

;listen = /run/php/php7.0-fpm.sock

listen = 127.0.0.1:9000

listen.allowed_clients = 127.0.0.1

Change nginx fastcgi params.

Now go tell Nginx to talk to PHP-FPM on the new sockets. You’ll need to do this for every site, snippet, or other included config file that directs PHP traffic.

location ~ \.php$ {

include snippets/fastcgi-php.conf;

# default: faux sockets via Unix file

#fastcgi_pass unix:/run/php/php7.0-fpm.sock;

# modified: actual TCP socket

fastcgi_pass 127.0.0.1:9000;

}

This article on servers for hackers will give you additional insight.

Look At PHP FPM Memory Consumption

Use a command like htop or ps to review memory consumption. For HTOP, the values include VIRT – the total memory available to a process, RES – the resident memory or memory used by the process, and SHR – the amount of memory that may be coming from shared processes.

htop

You typically want to look at RES as the amount of memory each process uses. Average it out. BUT you must know how your PHP and web process libraries are configured and the shared memory stack on your server. Most libraries in PHP are shared. That means a bulk of the code, which can be as much as 80% of the shared library memory, is only loaded ONCE, no matter how many PHP processes you activate.

Our setup averages 139M on resident memory and 123M on shared memory. That makes sense, as most of our web processes use the same PHP libraries and a massive chunk of identical PHP code. A data stack of 16M per PHP process is reasonable.

pmap

You can validate this by using the pmap command with the process id:

# sudo pmap 2963

This returns the entire memory map of process #2963

In the example below, 899704K of total memory is in use, but a large chunk is system libs, PHP libs, and web server lib shared code.

[SEE PIC AT THE TOP OF THIS PAGE]

The visibility that pmap provides may indicate libraries or features you may have enabled and can disable. We found a production server with xdebug libraries still active. Turning that off cut nearly 50M or resident memory from every process!

free -m

Looking at the system memory with free-m gives you a high-level overview of total system memory usage. It will report the total memory, used at the moment, free, shared memory, and buffers.

You will want to track buffers and memory used by non-PHP processes if you run other apps on your server, such as the database. Our web servers only serve web apps, so there is almost no additional overhead. That means I can use the buffer value in calculating how many PHP processes to spin up.

Calculate PHP Max Processes

The simple version of the formula:

Total system ram – used by apps ram – buffer ram/ram use per PHP app

16GB – 1GB (various system processes) – 500M (cache process) – 500M (php) = 14GB for PHP apps / 16M = about 800 max processes

In our example server from the cluster, a web-app-specific node, there is 16GB available, of which various server processes consume 1GB, 500MB of high-speed local cache, and another 500M is used by PHP and related shared code. That leaves 14G for PHP apps averaging 16M/process. Each node can handle around 800 simultaneous web requests or PHP-FPM children

PHP FPM Pool Parameters

Tuning the PHP configuration file is also essential. Here we are referencing PHP 7.4 and the FPM service on Ubuntu. The configuration file we are interested in lives at /etc/php/7.4/fpm/poold/www.conf

pm.max_children

Per the calculations above, this should be set based on the total available memory. We are now running on a server with 8GiB RAM , 1 GiB of which is allocated to system overhead and the main PHP engine itself. That leaves 7GiB for PHP application RAM. Our app now consumes 300MB per process.

Total RAM – System Overhead = 7G / 300M = 23.3 children max, so we’ll set that at 20.

pm.start_servers

Some online resources recommend this to be CPU cores x 4; however, we could not set it to more than the # of CPU cores.

pm.min_spare_servers

Some online resources recommend this to be CPU cores x 2; however, we could not set it to more than the # of CPU cores.

Reindexing in the database

Keep in mind that your PHP processes do not run in a vacuum. Most PHP apps running on web servers will talk to a database. Spinning up 800 PHP connections will likely need 800 database connections, even when using persistent connections. Each web session will demand its channel for most apps.

Make sure your database server is configured to handle all those connections. We run on Amazon Aurora database clusters, varying wildly in how many links they can drive. Make sure the max_connections on the database engine match how many you may throw at it.

Related Articles

Related