Tuning dynamic php-fpm settings

This is as much a note to self than anything else. Each time I need to change my php-fpm settings, I need to Google “php-fpm dynamic tuning” or something similar. With a little luck, next time I Google it, I'll find this page :)

Step one: Figuring out how much memory your typical PHP process uses

First we need to figure out how much memory a typical PHP process uses. This will inform the total number of processes that we’re going to run. We can do that with this nifty command:

ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\\n", sum/NR/1024,"M") }'

The ps bit will show all the current running php-fpm processes (including their memory consumption), and then the awk bit adds them all up and pretty-prints the value in MB.

In the end, this will print out a nice number for us. Something in the 40–60 MB range is to be expected with a typical Laravel app.

Step two: Deciding how much memory to give to PHP

This one is entirely up to you. You want to leave some memory for the other processes on your server. If the server is dedicated to running PHP only, you can dedicate most of your RAM to the php-fpm processes. On the other hand, if you’re also running a database server, redis, etc, you’re going to need to leave space for those to run.

Keep in mind that if you have queue workers running on your server, they'll each take up about the same amount of RAM as your other PHP processes. So, for example, if you have 10 queue workers running on your server and your processes take about 50 MB or RAM, that's another 500 MB of RAM that you need to set aside.

In my most recent case, I needed to account for the fact that sometimes we have other processes running that consume about 1 GB of RAM. To play it safe, I decided to reserve 2 GB of RAM for “system and other” processes. On a instance with 8 GB of RAM, that leaves us with 6 GB for PHP.

Now, let's let the computers do math for us:

GB
GB
MB

Suggested Settings:

; Run php-fpm in "dynamic" mode
pm = dynamic
; Set max_children to ([total RAM - reserved RAM]) / [average php-fpm process])
; Most recently: (1024 * (8 - 2)) / 60 = 102
pm.max_children = 100
; When php-fpm starts, have this many processes waiting for requests. Set to 50% of
; max on a server that's mostly responsible for running PHP processes
pm.start_servers = 50
; Minimum number spare processes php-fpm will create. In the case of a server
; dedicated to running PHP, we'll set this to about 1/3 of the remaining capacity
pm.min_spare_servers = 15
; Maximum number spare processes php-fpm will create. If more than this
; many processes are idle, some will be killed.
pm.max_spare_servers = 40
; After this many requests, a php-fpm process will respawn. This is useful
; to guard against memory leaks, but causes a small performance hit. Set to
; a high number (or 0) if you're confident that your app does not have any
; memory leaks (and that you're not using any 3rd-party libraries that have
; memory leaks), or set to a lower number if you're aware of a leak.
pm.max_requests = 500