Skip to main content
Both of these live on a server’s Runtime tab. Runtime is the software on the machine, as against the record of it: everything here is a file on the server and a service restart.

Several versions at once

A machine can have more than one PHP installed, and that is the point rather than an edge case. An application still on 8.2 and one on 8.5 can share a server, because each site’s nginx configuration names its own FPM socket. Each site picks its version independently.

Removing a version

A version still in use by a site cannot be removed. Nginx does not check that the socket it has been pointed at exists — it just fails the request — so a site left pointing at an uninstalled version answers 502 on every request without complaining anywhere you would look first. Move the sites off it, then remove it.

The CLI default

One installed version is the php on PATH. It is what a scheduled job or a daemon written as php artisan ... will get, and what somebody typing php -v over SSH sees. It is not what a site runs. A site’s requests go to its own FPM pool, and a deploy script gets its site’s version through $SHIPWAYS_PHP. Setting the CLI default is about the machine, not about any one site — and it is why a process written with the full path, /usr/bin/php8.4 artisan queue:work, is the safer form.

PHP configuration

Written into a drop-in that every installed version reads, so two versions on one machine cannot disagree about the limits. Applying restarts each FPM pool. If a pool does not come back up, the previous configuration is put back — a machine whose PHP will not start is worse than one whose upload limit is wrong.

Max file upload size

One field, because there are three settings and changing one of them is what makes the whole thing look broken. Setting it to N megabytes writes:
  • upload_max_filesize = NM — the largest single file.
  • post_max_size = N + 8 M — the largest whole request body.
  • nginx client_max_body_size = N + 8 m — the largest body nginx passes on.
The outer two carry 8 MB of headroom on purpose. Set all three equal and a file of exactly N megabytes still fails, because the multipart envelope around it — the boundaries, the field names, the ordinary form the file was attached to — pushes the body past post_max_size. PHP’s answer to that is to discard $_POST and $_FILES entirely and say nothing, which is a bug report about a form that “silently does nothing”. With the headroom, the limit actually reached is upload_max_filesize, and PHP reports it as UPLOAD_ERR_INI_SIZE against the field it happened on. Nginx’s own limit matters because nginx answers 413 before PHP is started at all. That is not a PHP error and does not appear in a PHP log, which sends people looking in the wrong place.

Max execution time

Sets max_execution_time, and nginx’s fastcgi_read_timeout to 10 seconds more. Nginx stops waiting after 60 seconds on its own. Raising only PHP’s limit gets you a 504 from nginx at the old boundary rather than a longer request, so both move together.

OPcache for production

Turns off file change detection, so PHP stops checking whether every file it has cached has changed since it cached it. Faster, and it means edits made directly on the server are not picked up until FPM reloads. Deployments reload it, so this is safe for anything Shipways deploys and awkward for anything edited in place — a WordPress install somebody FTPs into, for instance.