Websites & apps

Upgrade PHP to a newer version on your VPS

Upgrade a site from PHP 8.1 to 8.3 or 8.4 safely. Install the new PHP-FPM and matching extensions, carry over php.ini changes, switch Nginx or Apache, test, and remove the old version.

IntermediateLinux server5 min read

Old PHP versions stop getting security fixes, and new plugins and frameworks drop support for them. Upgrading does not have to be risky. Because you can install the new PHP-FPM next to the old one, you switch each site over with a one-line config change and can switch back just as fast. This guide uses PHP 8.1 to 8.3 as the example. The steps are the same for any pair of versions.

Before you start

  • A Linux VPS running Ubuntu 22.04/24.04 or Debian 12 with a sudo user, serving PHP through PHP-FPM.
  • The repository that provides the new version. On most servers that means Ondřej Surý's packages: Install multiple PHP versions on Ubuntu and Debian. On AlmaLinux or Rocky Linux, see the Remi section near the end.
  • A backup of your site files and database, taken today.
  • Confirmation that your apps support the new version. Check the release notes of your CMS, framework and important plugins.

Warning: A major PHP upgrade can break old code that uses removed functions. Always test on a staging copy or at least on one site first, and keep the old version installed until you are sure.

Record what you use now

List the installed packages for the current version:

Bash
dpkg -l | awk '/^ii/ && /php8.1/ {print $2}'

You will see names like php8.1-fpm, php8.1-mysql, php8.1-curl. You need the same set for the new version.

Also list the modules PHP has loaded, for comparison later:

Bash
php8.1 -m > ~/php81-modules.txt

Find any settings you changed from the defaults. Look for files you added to conf.d, such as 99-custom.ini, and print the active lines of your pool files:

Bash
ls -l /etc/php/8.1/fpm/conf.d/
grep -vE '^\s*(;|$)' /etc/php/8.1/fpm/pool.d/*.conf

If you edited php.ini directly, compare it with the package default to see your changes:

Bash
diff <(grep -vE '^\s*(;|$)' /usr/lib/php/8.1/php.ini-production) <(grep -vE '^\s*(;|$)' /etc/php/8.1/fpm/php.ini)

Install the new version and extensions

Build the install command from your list by swapping the version number:

Bash
dpkg -l | awk '/^ii/ && /php8.1-/ {print $2}' | sed 's/8\.1/8.3/' | xargs sudo apt install -y

Review the list apt shows before it installs. If a package does not exist for the new version, apt stops and names it. Some extensions were merged into PHP itself or renamed, so look up any that fail and drop or replace them.

Compare the modules:

Bash
php8.3 -m > ~/php83-modules.txt
diff ~/php81-modules.txt ~/php83-modules.txt

Any line starting with < is a module you had before and do not have now.

Carry over your settings

Do not copy the old php.ini over the new one. Defaults change between versions. Instead, put your custom values in a small override file for the new version:

Bash
sudo nano /etc/php/8.3/fpm/conf.d/99-custom.ini
INI
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120

If you created custom pools (one per site), copy each pool file across and change the socket path inside it:

Bash
sudo cp /etc/php/8.1/fpm/pool.d/example.conf /etc/php/8.3/fpm/pool.d/
sudo sed -i 's/php8\.1-fpm/php8.3-fpm/g' /etc/php/8.3/fpm/pool.d/example.conf

Check the new config and start the service:

Bash
sudo php-fpm8.3 -t
sudo systemctl enable --now php8.3-fpm

test is successful means the configuration parsed cleanly.

Switch the web server

Nginx

In each site's server block, change the socket in fastcgi_pass:

Nginx
fastcgi_pass unix:/run/php/php8.3-fpm.sock;

Then test and reload:

Bash
sudo nginx -t && sudo systemctl reload nginx

Apache with PHP-FPM

If the site uses the global php8.1-fpm conf, swap it:

Bash
sudo a2disconf php8.1-fpm
sudo a2enconf php8.3-fpm
sudo apachectl configtest && sudo systemctl reload apache2

If you set the handler per virtual host, change the socket path in that site's SetHandler line instead, then run the same test and reload.

Command line and cron

Point the php command at the new version, and update any cron jobs or scripts that call php8.1 directly:

Bash
sudo update-alternatives --set php /usr/bin/php8.3
crontab -l | grep php

Test, then keep a way back

  1. Load the home page, a few inner pages, the admin area, a form and any checkout.
  2. Watch the PHP-FPM log for new warnings while you click around:
Bash
   sudo tail -f /var/log/php8.3-fpm.log
  1. Check your app's own error log too, such as wp-content/debug.log for WordPress.

If something breaks, change the socket back to php8.1-fpm.sock and reload. The old version is still running, so the rollback takes seconds.

Remove the old version

After a few days without problems, remove the old packages to free memory and stop patching them:

Bash
sudo systemctl disable --now php8.1-fpm
sudo apt purge 'php8.1*'
sudo apt autoremove

Warning: apt purge deletes the old version's configuration in /etc/php/8.1. Copy anything you might still want first.

AlmaLinux and Rocky Linux with Remi

With Remi module streams, the default version is replaced in place. Switch the stream, then sync packages:

Bash
sudo dnf module switch-to -y php:remi-8.3
sudo systemctl restart php-fpm

Because this replaces the old version, test it on a staging server first, or install the new version alongside as php83-php-fpm and move sites one at a time. See Install PHP 8.x on AlmaLinux and Rocky Linux with Remi.

Check it works

  • php -v shows the new version.
  • A temporary page with <?php echo PHP_VERSION; shows the new version through the web server. Delete it afterwards.
  • systemctl status php8.3-fpm shows active (running).

Troubleshooting

  • 502 Bad Gateway after switching: the new FPM service is not running, or the socket path has a typo. Check ls /run/php/.
  • "Call to undefined function" errors: an extension is missing for the new version, such as mysqli or gd. Install the php8.3- package and restart PHP-FPM.
  • Deprecation warnings flood the page: set display_errors = Off in production and update the code or plugin.
  • Uploads fail after the upgrade: your old upload limits were not carried over. Add them to 99-custom.ini.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.