Skip to content

Size the PHP-FPM pool

$ grim tune --auto

Under load the app answers slowly while the server still has memory to spare, or the opposite: the box starts swapping because PHP was allowed more workers than it has room for. Both come down to one number, how many PHP-FPM workers may run at once, and the right value depends on the machine you rent, not on the project.

grim tune writes that number, and the few that go with it, into a small config file the production stack mounts into the app container. With --auto it measures the server first.

Usage

$ grim tune --auto     # measure the server, save the values, write the file
$ grim tune            # write the file from grim.json, or from the defaults

Run it from the project directory. --auto connects as the deploy user to the project's server, so the project must already be registered there.

Arguments and options

Generate PHP-FPM performance config for production

Usage

grim tune [--auto]

Options

Option Description
--auto Auto-detect optimal values from server specs via SSH

What it actually does

  1. Collects the values. Without --auto they are the defaults, overlaid with whatever the performance block of grim.json sets.
  2. With --auto, measures the server. CPU cores from nproc, available memory from free -m, and the current memory use of the app container when it is running. It prints all three.
  3. With --auto, calculates the pool. pm.max_children is 70 % of the available memory divided by 50 MB per worker, kept between 5 and 100. pm.start_servers is a quarter of that, pm.min_spare_servers an eighth, pm.max_spare_servers a half. The result is saved as the performance block of grim.json.
  4. Writes docker/php-fpm-production.conf. A [www] pool with pm, the four worker counts and pm.max_requests. The file says not to edit it by hand, because the next run replaces it.
  5. Prints a summary of the values and the file name.

The values

Key in performance Default Reaches the config file
pm dynamic Yes
pm.max_children 20 Yes
pm.start_servers 5 Yes
pm.min_spare_servers 3 Yes
pm.max_spare_servers 10 Yes
pm.max_requests 500 Yes
memory_limit 512M No, shown in the summary only
opcache.memory_consumption 256 No, shown in the summary only
opcache.jit_buffer_size 128M No, shown in the summary only

The last three are fixed in the production image. Changing them in grim.json changes what the summary prints and nothing else.

Pitfalls

The file does not travel with a deploy

The production stack mounts ./docker/php-fpm-production.conf from the project's directory on the server. It is not inside the image, and grim deploy uploads the compose file and the nginx config but not this one. The summary's Next row names build and deploy; neither delivers the file. Copy it yourself and restart the app container.

$ scp docker/php-fpm-production.conf deploy@203.0.113.10:/opt/shop/docker/
$ grim remote:exec docker compose restart app

--auto rewrites the whole block

It saves the defaults plus the five values it calculated. A pm mode, a memory_limit or any other key you had set by hand in performance is replaced by its default. Look at git diff grim.json afterwards.

--auto measures the default environment

The host is deploy.host from grim.json, or else the project's default environment in ~/.grim/servers.json. There is no argument for another one. No deploy host configured. means neither exists; register the project with grim server:add-project first.

Available memory is a snapshot

The calculation uses what was free at the moment you ran it, with the app's own workers counted as used. Measure at a quiet hour, and on a server shared by several projects expect a lower number than the machine's size suggests.

After it finishes

Commit grim.json and the generated file, get the file onto the server as shown above, and check that PHP-FPM took it.

$ grim remote:exec docker compose exec -T app php-fpm -tt