> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shipways.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP versions and configuration

> Running several versions on one machine, and the three limits people SSH in to change.

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](/sites/creating) 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](/processes/scheduled-jobs) or a
[daemon](/processes/daemons) 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` = *N*M — 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](/sites/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.
