Each Ubuntu and Debian release ships one PHP version (e.g. Ubuntu 24.04 has PHP 8.3). Real servers often need more: an older app that isn’t ready for PHP 8.3, a new app that wants PHP 8.4, or a gradual migration across many sites. The widely used repositories maintained by Ondřej Surý provide every supported PHP version for Debian and Ubuntu, installable side by side.
1. Add the repository
Ubuntu (PPA):
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
Debian:
sudo apt install -y lsb-release ca-certificates curl
sudo curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list
sudo apt update
This is a third-party repository — well established, but outside your distribution’s own support. Prefer supported PHP versions; end-of-life versions get no security fixes.
2. Install the versions you need
Packages are prefixed with the version:
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.2-gd php8.2-intl
sudo apt install -y php8.4-fpm php8.4-cli php8.4-mysql php8.4-curl php8.4-mbstring php8.4-xml php8.4-zip php8.4-gd php8.4-intl
Each version has its own:
- service:
php8.2-fpm,php8.4-fpm - socket:
/run/php/php8.2-fpm.sock,/run/php/php8.4-fpm.sock - config:
/etc/php/8.2/…,/etc/php/8.4/…
Install the same extensions for every version a site might use — see installing PHP extensions.
3. Choose the version per site
Nginx: point the site’s fastcgi_pass at the version’s socket:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
Apache (with PHP-FPM):
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
</FilesMatch>
Reload the web server. Different sites on the same server can now run different PHP versions. See Nginx with PHP-FPM and Apache with PHP-FPM.
4. Switch the CLI default
The php command (used by Composer, WP-CLI, cron jobs, artisan) has its own default:
sudo update-alternatives --config php
php -v
Or call a specific version explicitly — safest in cron jobs and scripts:
php8.2 /var/www/legacy/artisan schedule:run
/usr/bin/php8.4 /usr/local/bin/composer install
Make sure the CLI version used for maintenance matches the version the site runs on; Composer resolves dependencies against the PHP version it runs under.
5. Upgrade a site to a new PHP version
- Check compatibility: the app’s requirements, plugin/library support. Tools like PHPCompatibility (PHP_CodeSniffer) or Rector help for custom code.
- Install the new version and the same extensions.
- Test on a staging copy pointed at the new socket (see WordPress staging).
- Copy custom
php.inioverrides and pool settings to the new version’s directories (/etc/php/8.4/fpm/conf.d/,pool.d/). - Switch the production site’s socket, reload, and watch the error logs.
- Keep the old version installed briefly for rollback; then remove it.
6. Remove an old version
sudo apt purge 'php8.1*'
sudo apt autoremove
Make sure no site’s config still points at its socket first: grep -r "php8.1" /etc/nginx /etc/apache2.
Checking what’s installed and running
ls /etc/php/
systemctl list-units 'php*-fpm*' --no-pager
ls -l /run/php/
Memory considerations
Each running PHP-FPM version starts its own master and worker pool. On a small VPS, keep only the versions you actually use, and size pm.max_children per pool so the totals fit in RAM — see PHP-FPM tuning.
Related: php.ini settings and WordPress PHP 8 compatibility.