PHP 7.4 and 8.3 are both on the server, php -v reports 8.3, and the cron job for the old application fails anyway.

The command line, the FPM service and individual vhosts each decide independently which version they use. Changing one leaves the other two exactly as they were.

Several versions side by side is not an exotic setup, either. As soon as you source PHP from Sury it is the normal state: the packages carry their version in the name and do not interfere.

Which versions are installed

ls /etc/php/
systemctl list-units 'php*-fpm*' --no-pager

The first lists the configuration directories, which is every installed version. The second shows which FPM services are actually running — an installed version need not be started.

The three layers

Three places decide, and they do it independently of each other:

LayerDecidesControlled by
Command linephp in the terminal, cron jobs, Composerupdate-alternatives
FPM servicewhich service runs at allsystemctl per version
Vhostwhich version a site usessocket in the web server

The three are independent. A server can use 8.3 on the command line while one site runs on 7.4 and another on 8.2.

The command-line default

Debian manages this through update-alternatives. Interactively:

sudo update-alternatives --config php

Or set directly:

sudo update-alternatives --set php /usr/bin/php8.3

Check with php -v. Every version also stays reachable under its full name:

php7.4 -v
php8.3 -v
Tip
In cron jobs and startup scripts, always write the version out — php8.3 /path/script.php rather than php /path/script.php. The job then survives someone changing the default later. This is one of the most common reasons for tasks that fail unnoticed for months.

Besides php, other alternatives manage their own tools, phar and phar.phar among them. update-alternatives --get-selections | grep php lists them all.

FPM services

Each version brings its own service and its own socket:

/run/php/php7.4-fpm.sock
/run/php/php8.3-fpm.sock

Start them per version:

sudo systemctl enable --now php8.3-fpm
sudo systemctl status php7.4-fpm

ls -la /run/php/ shows which sockets actually exist. Only those can be addressed from the web server — a socket for a version whose service is not running simply is not there.

The configuration is separate too. Every version has its own files:

/etc/php/7.4/fpm/php.ini
/etc/php/7.4/fpm/pool.d/www.conf
/etc/php/8.3/fpm/php.ini
/etc/php/8.3/fpm/pool.d/www.conf

A setting you change in 8.3 does not apply to 7.4.

A version per vhost in Apache

Inside the vhost you decide which socket PHP files go to:

<VirtualHost *:443>
    ServerName old.example.com
    DocumentRoot /var/www/old

    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost/"
    </FilesMatch>
</VirtualHost>

A second vhost on the same machine then points at php8.3-fpm.sock. The required modules have to be enabled:

sudo a2enmod proxy_fcgi setenvif
sudo systemctl reload apache2

As long as mod_php is enabled it claims every PHP file, and the vhost setting never takes effect. Disable it first:

sudo a2dismod php8.3
sudo systemctl restart apache2

A version per vhost in nginx

Here the decision sits in fastcgi_pass:

server {
    server_name old.example.com;
    root /var/www/old;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    }
}

For the next server block you only swap the socket. Then test and reload:

sudo nginx -t && sudo systemctl reload nginx

Separate FPM pools instead of one shared one

By default a pool called www serves every site on a version. For separate applications a pool per site is worth it, because they then run as different system users and cannot read each other's files.

To do that, add a second file beside www.conf in the same directory, say /etc/php/8.3/fpm/pool.d/customer1.conf, with its own pool name, its own user, its own group and its own listen socket. Then restart the service and point the vhost at the new socket.

A compromised site then cannot reach another site's files, and a pool that jams does not drag the rest down with it.

Checking which version applies where

php -v
php8.3 -v
ls -la /run/php/
systemctl list-units 'php*-fpm*' --no-pager

For a specific site the most reliable probe is a short script calling phpinfo() that you delete immediately afterwards. Besides the version it also shows which php.ini was actually loaded.

Caution
A forgotten phpinfo() script exposes paths, extensions, version numbers and configuration values. Do not park it under a name like info.php, and do not leave it behind.

Getting rid of a version

sudo systemctl disable --now php7.4-fpm
sudo apt purge 'php7.4-*'

Check first that no vhost and no cron job still points at it, or the site serves an error page instead of PHP afterwards. /etc/php/7.4/ survives apt remove and only disappears with purge.

Troubleshooting

ProblemCause and fix
php -v differs from the website's versionCommand line and vhost are separate. update-alternatives --config php only affects the terminal.
Cron job uses the wrong versionThe job says php instead of php7.4. Write the version out.
The vhost setting has no effectmod_php is still enabled and intercepts every PHP file. Disable it with a2dismod.
The web server reports a missing socketThat version's FPM service is not running. Start it with systemctl enable --now.
A php.ini change does nothingThe wrong version's file was edited, or the wrong branch of cli and fpm.
An extension is missing in one version onlyExtensions install per version: php7.4-gd in addition to php8.3-gd.
Error page after removing a versionA vhost still points at the deleted socket.

Frequently asked questions about multiple PHP versions

Can PHP 7.4 and 8.3 run at the same time?

Yes. The packages carry the version in their names, have separate configuration directories and their own FPM services. They do not interfere.

How do I switch the PHP version on the command line?

With sudo update-alternatives --config php interactively, or --set php /usr/bin/php8.3 directly. That only affects the terminal and cron jobs, not your websites.

How do I give a single site a different version?

Through the FPM socket. In Apache with SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost/" inside the vhost, in nginx through fastcgi_pass in the matching location block.

Why does nothing change after editing the vhost?

Usually mod_php is still enabled in Apache. It claims every PHP file before the vhost setting applies. Disable it with a2dismod php<version> and restart Apache.

Do I have to install extensions for every version separately?

Yes. php8.3-gd only applies to 8.3; 7.4 additionally needs php7.4-gd. Packages without a version number land on the default version.