You can run multiple PHP versions on Ubuntu at the same time by installing each version as its own PHP-FPM service and pointing each Nginx site at that version's FPM socket. This guide installs two PHP versions, sets the command-line default, and configures two sites to use different runtimes.
This is useful when one application needs a newer PHP release while a legacy site still depends on an older branch. Each PHP version runs as a separate service, so upgrading one site does not force an upgrade on the other.
Before you start
You need:
- An Ubuntu 22.04 or 24.04 Linux VPS with sudo access. If you do not have a sudo user yet, see create a sudo user on Ubuntu, Debian, AlmaLinux and Rocky Linux.
- Nginx installed and running. The examples use Nginx server blocks. If you use Apache, the socket path is the same, but the proxy configuration differs; see run PHP-FPM with Apache using proxy_fcgi and mpm_event.
- A domain or subdomain pointing to the server. The examples use
example.comandlegacy.example.comwith server IP203.0.113.10. - Access to your VPS provider's control panel for power controls, console access and OS reinstall if needed.
Warning: Adding a third-party package repository changes where APT gets PHP packages. Use the repository only on a server you control, and check the vendor's documentation for supported Ubuntu releases before you add it.
The examples install PHP 8.2 and PHP 8.3. Adjust the version numbers to the branches your applications support. PHP release support changes over time, so check the PHP project's supported versions page before choosing a branch.
Install multiple PHP versions with PHP-FPM
Add the PHP package repository, then install both PHP-FPM services and the extensions your sites need.
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt updateInstall PHP 8.2 and PHP 8.3 with FPM, CLI and common extensions:
sudo apt install -y \
php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-mbstring php8.2-xml php8.2-zip \
php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl php8.3-mbstring php8.3-xml php8.3-zipEnable and start both FPM services:
sudo systemctl enable --now php8.2-fpm php8.3-fpmCheck that each service is active:
systemctl status php8.2-fpm --no-pager
systemctl status php8.3-fpm --no-pagerList the FPM sockets:
ls -l /run/php/You should see sockets such as php8.2-fpm.sock and php8.3-fpm.sock. Nginx connects to these Unix sockets. If a socket is missing, the matching FPM service is not running or its pool configuration failed to load.
Set the command-line PHP default to the version you want for SSH work:
sudo update-alternatives --set php /usr/bin/php8.3
php -vThe php -v output should show PHP 8.3. This changes only the CLI default. It does not change which PHP version Nginx uses for a website.
Point each Nginx site at a PHP version
Each Nginx server block chooses a PHP version by using a different fastcgi_pass socket. Create a site that uses PHP 8.3:
sudo mkdir -p /var/www/example.com/public
echo '<?php echo PHP_VERSION;' | sudo tee /var/www/example.com/public/version.php
sudo chown -R www-data:www-data /var/www/example.comCreate /etc/nginx/sites-available/example.com:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}Create a second site that uses PHP 8.2:
sudo mkdir -p /var/www/legacy.example.com/public
echo '<?php echo PHP_VERSION;' | sudo tee /var/www/legacy.example.com/public/version.php
sudo chown -R www-data:www-data /var/www/legacy.example.comCreate /etc/nginx/sites-available/legacy.example.com:
server {
listen 80;
server_name legacy.example.com;
root /var/www/legacy.example.com/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}Enable both sites and test the Nginx configuration:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo ln -s /etc/nginx/sites-available/legacy.example.com /etc/nginx/sites-enabled/legacy.example.com
sudo nginx -t
sudo systemctl reload nginxIf nginx -t reports a duplicate default server or a missing file, fix that before reloading. A reload is safer than a restart because it keeps existing connections open.
Tip: Keep the socket path inside the location ~ \.php$ block. If you copy a server block from another site, change fastcgi_pass to the correct PHP version socket.
Check it works
Request the test file from each hostname. If DNS is not live yet, send the Host header to the server IP:
curl -sS -H 'Host: example.com' http://203.0.113.10/version.php
curl -sS -H 'Host: legacy.example.com' http://203.0.113.10/version.phpThe first response should show an 8.3 version string and the second should show an 8.2 version string. You can also check the active FPM sockets:
ss -xl | grep phpFor a quick service check:
systemctl is-active php8.2-fpm php8.3-fpmBoth services should return active. If one site returns the wrong PHP version, inspect its Nginx server block and confirm the fastcgi_pass line points to the intended socket.
Troubleshooting
- 502 Bad Gateway after reloading Nginx → Nginx cannot reach the FPM socket. Check
ls -l /run/php/, confirm the service is running withsystemctl status php8.3-fpm, and compare the socket path in the site config. Read/var/log/nginx/error.logfor the exact connect error. - “File not found” or “Primary script unknown” → The Nginx
rootpath or the PHP script path is wrong. Confirm the file exists under the configuredrootand that thelocation ~ \.php$block includessnippets/fastcgi-php.conf. - The site still uses the old PHP version → The server block still points at the old socket, or another matching server block is being used. Run
grep -R "fastcgi_pass" /etc/nginx/sites-enabled/and reload Nginx after fixing the file. - Permission denied connecting to the socket → The FPM pool user or socket permissions do not match the Nginx worker user. On Ubuntu, Nginx usually runs as
www-data; check the pool file under/etc/php/8.3/fpm/pool.d/www.confand the socket owner withls -l /run/php/php8.3-fpm.sock. - APT cannot find
php8.3-fpm→ The PHP repository is missing or does not support your Ubuntu release. Runapt-cache policy php8.3-fpm, confirm the repository was added, and check the vendor's documentation for supported releases.
Next steps
- Use a different PHP version for each virtual host without changing global defaults: use a different PHP version per website in Nginx.
- Tune worker limits after you add more pools: tune PHP-FPM pools (pm.max_children) for your RAM.
- If you run Apache instead of Nginx: run PHP-FPM with Apache using proxy_fcgi and mpm_event.
