How to upgrade a PHP server to the next Ubuntu or Debian release

Ubuntu 22.04 to 24.04, or Debian 12 to 13. The upgrade itself is a few commands. The part that bites is PHP: it moves to a new version, and your sites still point at the old one.

Expect "502 Bad Gateway" after the upgrade. In our tests, both upgrades finished without errors, and both sites went down after the reboot, because Nginx still sent requests to the old PHP version. Step 6 shows the fix. Plan a quiet hour, not five minutes.

When to upgrade

Each release gets free security fixes for a few years. After that, you're on your own. Check the dates on the official pages (Ubuntu, Debian):

  • Ubuntu 22.04: standard security updates until May 2027. Ubuntu 24.04 gets them until May 2029.
  • Debian 12: regular security support ended on 11 July 2026. It now gets Debian LTS fixes until 30 June 2028, for fewer packages. Debian 13 gets regular support until 9 August 2028.

Ubuntu only offers an upgrade from one LTS release to the next one. The examples here move Ubuntu 22.04 to 24.04 and Debian 12 to 13. Change the names to yours.

Think about a new server instead. Setting up a fresh 24.04 or 13 server and moving your sites to it takes longer, but the old server keeps running until the new one works. If the server has years of hand-made changes nobody remembers, that's often the safer path.

1. Back up and get ready

  • Take a full backup of the files and databases, and test that you can restore it. See our backup guide.
  • Take a snapshot of the whole server too, if your provider offers it. If the upgrade goes badly, rolling back the snapshot is the fastest way back.
  • Find your provider's web console (often called "console" or "VNC" in their panel). If SSH stops working, that's how you get back in.
  • Check free disk space with df -h /. The upgrade downloads a lot of packages, often more than 1 GB.

Then write down what you run today, so you can compare later. Which PHP versions, and which sockets your sites use:

ls /etc/php/
sudo grep -Rn fastcgi_pass /etc/nginx/sites-enabled/

Use -R with a capital R. The files in sites-enabled are usually links, and grep -r skips links. In our test it printed nothing at all.

Also list the extra package sources that don't come from Ubuntu or Debian, such as Ondřej Surý's PHP packages:

ls /etc/apt/sources.list.d/

2. What changes for PHP sites

Each release ships one PHP version. Ubuntu 22.04 has PHP 8.1 and Ubuntu 24.04 has PHP 8.3. Debian 12 has PHP 8.2 and Debian 13 has PHP 8.4. What happens to your sites depends on where your PHP came from:

  • PHP from Ubuntu or Debian (you installed php-fpm): the upgrade installs the new version and removes the old one. In our Debian test, PHP 8.4 came in and PHP 8.2 went out. But the Nginx file still said php8.2-fpm.sock, so after the reboot the site showed "502 Bad Gateway".
  • PHP from Ondřej Surý's packages (ppa:ondrej/php or packages.sury.org): the old version stays installed, but it no longer gets updates. In our Ubuntu test, the site kept running on PHP 8.1, built for 22.04, with the package source turned off. That looks fine, but it's a trap: no more security fixes.

Either way, your own settings stay behind in the old folder, such as /etc/php/8.1/fpm/conf.d/ and your own pools in /etc/php/8.1/fpm/pool.d/. The new version starts with default settings.

Check your app first. Make sure your WordPress plugins, theme, or Laravel version run on the new PHP. Our guide to moving a site to a newer PHP version shows how to test that. If you can, test the site on the new version before you upgrade the server.

3. Start a session that survives a dropped connection

If your SSH connection drops halfway, the upgrade must keep going. Run it inside tmux (or screen):

sudo apt install tmux
tmux new -s upgrade

If the connection drops, log in again and run tmux attach -t upgrade to get back to it. On Ubuntu, the upgrade tool also runs itself inside screen. If you lose it, run sudo do-release-upgrade again: it reconnects to the running upgrade instead of starting a new one.

Ubuntu only: when you run the upgrade over SSH, Ubuntu asks if you want to go on, and then starts a spare SSH server on port 1022. If the normal SSH breaks during the upgrade, you can still log in on that port. It doesn't open the port in your firewall for you. To use it, open it for the time of the upgrade (with UFW, see our firewall guide):

sudo ufw allow 1022/tcp

If your provider has a firewall in its panel, open it there too. Close it again in step 8.

4a. Upgrade Ubuntu 22.04 to 24.04

First, bring 22.04 fully up to date. The upgrade tool refuses to start otherwise:

sudo apt update
sudo apt full-upgrade

If the file /run/reboot-required exists, reboot now and start tmux again. Then check that the upgrade is offered, and start it:

do-release-upgrade -c
sudo do-release-upgrade

It shows a summary first: how many packages it installs, upgrades and removes. Read it before you say yes. Along the way it may ask:

  • About extra package sources: Ubuntu turns them off for the upgrade on its own, for example ppa:ondrej/php. You turn them back on in step 7.
  • About changed settings files: see step 5.
  • "Remove obsolete packages?" These are packages the new release no longer has. With Ondřej's PHP, the old PHP version is on that list. If your sites still use it, say no for now and remove it after step 6.
  • To restart at the end. Say yes.

4b. Upgrade Debian 12 to 13

Debian has no upgrade tool. You point apt at the new release yourself. First bring Debian 12 fully up to date:

sudo apt update
sudo apt full-upgrade

Turn off package sources that don't come from Debian. For example, Ondřej Surý's PHP packages:

sudo mv /etc/apt/sources.list.d/php.list /root/php.list.disabled

This command lists the installed packages that don't come from Debian, so you know what to look at later:

apt list '?narrow(?installed, ?not(?origin(Debian)))'

Now replace the old release name (bookworm) with the new one (trixie) in your package sources. They live in /etc/apt/sources.list on older installs and in /etc/apt/sources.list.d/debian.sources on newer ones. This finds and changes both:

grep -rl bookworm /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null
sudo sed -i 's/bookworm/trixie/g' $(grep -rl bookworm /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null)
grep -rn trixie /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

Then upgrade in two steps, as the Debian release notes suggest. The first step does the easy part, the second does the rest:

sudo apt update
sudo apt upgrade --without-new-pkgs
sudo apt full-upgrade
sudo reboot

To see first how much it downloads and whether there's room: sudo apt -o APT::Get::Trivial-Only=true full-upgrade. It ends with an error on purpose, so nothing is installed. Look at the "Need to get" and "disk space" lines above it.

5. Settings file questions

If you changed a settings file and the new release has a new version of it, the upgrade stops and asks what to do. For example, for /etc/nginx/nginx.conf. You get these choices:

  • Keep your version (N, the default). Your changes stay. This is usually the right answer for files you edited on purpose.
  • Take the new version (Y). Your changes are lost, but a copy is saved next to it with .dpkg-old at the end.
  • Show the differences (D). Press q to go back to the question.

When you keep yours, the new version is saved next to it with .dpkg-dist at the end. After the upgrade, find them all and compare:

sudo find /etc -name '*.dpkg-dist' -o -name '*.dpkg-old' -o -name '*.ucf-dist'
diff /etc/nginx/nginx.conf /etc/nginx/nginx.conf.dpkg-dist

In our Debian test, the new nginx.conf turned off old TLS versions (1.0 and 1.1). That's worth copying into yours. Delete the extra files once you've looked at them.

Some questions come in a blue box instead (for example for sshd_config). The choices mean the same thing: "keep the local version" is yours.

6. Fix your PHP sites

After the reboot, check what runs and what failed:

cat /etc/os-release | head -2
systemctl --failed
ls /run/php/

If you see only the new socket, such as php8.4-fpm.sock, while Nginx still points at the old one, the site shows "502 Bad Gateway". The Nginx error log says so clearly:

connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)

If the new PHP isn't installed yet (Ondřej's packages, as in our Ubuntu test), install the same packages for it. This swaps the version number in each name:

sudo apt install $(dpkg -l | awk '/^ii/{print $2}' | grep php8.1 | sed 's/8.1/8.3/')

Copy your settings into the new version's folder, and test them:

sudo cp /etc/php/8.2/fpm/conf.d/99-custom.ini /etc/php/8.4/fpm/conf.d/
sudo php-fpm8.4 -t
sudo systemctl restart php8.4-fpm

If you use one PHP-FPM pool per site, copy those pool files from /etc/php/8.2/fpm/pool.d/ as well.

Point Nginx at the new socket. Find every site that names the old version, change it, and reload:

sudo grep -Rln php8.2 /etc/nginx/sites-enabled/
sudo sed -i 's|php8.2-fpm.sock|php8.4-fpm.sock|' /etc/nginx/sites-available/shop
sudo nginx -t && sudo systemctl reload nginx

Run sed on the file in sites-available, not on the link in sites-enabled. Otherwise sed replaces the link with a copy, and the two drift apart. Do the same for cron jobs and workers that call php8.2 directly (see step 8 of the PHP version guide).

Then open every site and click through the important pages: login, forms, checkout and the admin area. Watch the logs while you do: sudo tail -f /var/log/nginx/error.log /var/log/php8.4-fpm.log.

7. Turn extra package sources back on

Only do this if you still need them. They must point at the new release.

Ubuntu: the upgrade left the source in place, already changed to noble, but with Enabled: no. Find it and turn it back on:

grep -l 'Enabled: no' /etc/apt/sources.list.d/*
sudo sed -i 's/^Enabled: no/Enabled: yes/' /etc/apt/sources.list.d/ondrej-ubuntu-php-jammy.sources
sudo apt update

The file name still says jammy, but look inside: the line Suites: noble is what counts. Once it's back on, apt takes PHP from Ondřej's packages again, also the version that came with Ubuntu.

Debian: put the file back with the new release name:

sudo sed 's/bookworm/trixie/' /root/php.list.disabled | sudo tee /etc/apt/sources.list.d/php.list
sudo apt update

8. Clean up

Remove packages that nothing needs any more. Read the list before you say yes:

sudo apt autoremove

See which installed packages the new release doesn't have:

apt list '?obsolete'

The old PHP may show up here. When no site uses it any more (step 6), keep a copy of its settings and remove it:

sudo cp -a /etc/php/8.2 /root/php8.2-config-backup
sudo apt purge 'php8.2-*'

On Ubuntu, close the spare SSH port again: sudo ufw delete allow 1022/tcp, and in your provider's firewall too.

Last, check that security updates still install on their own. See our guide to automatic security updates.

If something goes wrong

  • "502 Bad Gateway": Nginx points at a PHP socket that's gone. Compare ls /run/php/ with sudo grep -Rn fastcgi_pass /etc/nginx/sites-enabled/ (step 6). For other causes, see our guide to fixing 502 and 504 errors.
  • The site works but behaves differently: it runs on the new PHP with default settings. Copy your settings over (step 6).
  • "Call to undefined function": an extension is missing for the new version, such as php8.4-gd. Install it and restart PHP-FPM.
  • Ubuntu says "Please install all available updates": run sudo apt update && sudo apt full-upgrade and reboot first (step 4a).
  • "No new release found": check /etc/update-manager/release-upgrades. It should say Prompt=lts.
  • The SSH connection dropped: log in again and run tmux attach -t upgrade (on Ubuntu, sudo do-release-upgrade also reconnects). If SSH doesn't answer at all, try port 1022 (Ubuntu) or your provider's web console.
  • The server doesn't come back after the reboot: use the web console. If you can't fix it, roll back the snapshot.

Official sources

Next steps: run through the security checklist for a PHP server on the upgraded system, and check which PHP versions still get security fixes.

ServerNest is being built to show which release and PHP version each server runs, and to warn you before support ends. Join early access to try it first.

← Back to the security blog