How to Upgrade Ubuntu Server to a New LTS Release (Without Losing a Weekend)

Every two years a new Ubuntu LTS arrives, and the one you installed gets five years of support from its own release. You don't have to upgrade on day one — in fact you shouldn't — but eventually the newer PHP, Python, kernel and Docker packages are worth having, and an unsupported release gets no security fixes at all. The upgrade itself is one command; the preparation is what makes it uneventful.
Quick reference
| Step | What |
|---|---|
| Which version am I on? | lsb_release -a |
| Wait for | the .1 point release (about 3 months after launch) |
| Back up | restic/rsync snapshot, plus a VM snapshot if it's a VM |
| Update first | sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y && sudo reboot |
| Upgrade | tmux then sudo do-release-upgrade |
| Afterwards | re-add third-party repositories, check services, reboot |
1. Is it time?
lsb_release -a
do-release-upgrade only offers the next LTS once its first point release (24.04.1, 26.04.1) is out, which is deliberate: the early bugs are fixed by then. Don't force it with -d on a server.
2. Back up
A release upgrade touches thousands of packages. Before it:
- A restic snapshot or rsync of /etc, /home, /var/www, ~/docker and database dumps (tutorials 23 and 55).
- If the server is a VM (Proxmox, cloud), take a snapshot — a five-second rollback beats any restore.
- dpkg --get-selections > ~/packages-before.txt so you can see what was installed.
3. Get fully up to date on the current release
sudo apt update && sudo apt full-upgrade -y
sudo apt autoremove -y
sudo reboot
The upgrader refuses to run with pending updates, and a reboot proves the server comes back on the old release before you change it.
4. Third-party repositories
Docker, Tailscale, Nginx, PostgreSQL — anything from /etc/apt/sources.list.d/. The upgrader disables them all and uses Ubuntu's versions, which can downgrade things. Note what's there:
ls /etc/apt/sources.list.d/
You'll re-enable them afterwards with the new release's codename.
5. Run the upgrade inside tmux
A dropped SSH session mid-upgrade is the one thing that can genuinely hurt. So:
tmux new -s upgrade
sudo do-release-upgrade
It warns about SSH and opens a fallback SSH on port 1022 — allow it: sudo ufw allow 1022/tcp in another window if asked. Then it calculates changes and asks to continue. Say y. Expect 20 to 60 minutes.
6. Answer the prompts
You'll be asked about configuration files you've changed (sshd_config, nginx.conf, jail.local, 50unattended-upgrades). The choices:
- Keep the local version currently installed — almost always the right answer for files you edited.
- Install the package maintainer's version — only for files you never touched.
- Show the differences — if unsure.
For sshd_config, keep yours; otherwise password login could switch back on. Services ask to restart: Yes.
At the end it offers to remove obsolete packages (y) and reboot (y).
7. Afterwards
lsb_release -a
systemctl --failed
docker ps
sudo ufw status
Re-enable third-party repositories: the upgrader left them in sources.list.d with # disabled on upgrade and the old codename. Edit each, change jammy to noble (or whatever lsb_release -cs now says), remove the comment, then:
sudo apt update && sudo apt full-upgrade -y
That brings Docker, Tailscale and the rest back to their own current versions. Delete the 1022 firewall rule. Check PHP-based sites (the PHP version changed — update the fastcgi_pass socket in Nginx, tutorial 30).
Rolling back
There's no undo in Ubuntu itself. Rollback is the VM snapshot or a reinstall plus restore — which is why step 2 isn't optional.
Troubleshooting
"No new release found." The point release isn't out, or Prompt=lts isn't set in /etc/update-manager/release-upgrades. Patience is correct here.
"Please install all available updates" loop. sudo apt full-upgrade, sudo apt autoremove, reboot, retry. Held packages (apt-mark showhold) can block it; unhold for the upgrade.
Lost the SSH session anyway. Reconnect and tmux attach -t upgrade. If tmux isn't there, sudo dpkg --configure -a and sudo apt full-upgrade finish the job.
A service won't start after. Its config format changed. journalctl -u name gives the line; compare with the .dpkg-dist file the upgrader left next to your config.
Wrapping up
Wait for the point release, back up and snapshot, update and reboot, upgrade inside tmux, keep your own config files, re-enable third-party repositories afterwards. An hour, most of it watching a progress bar, every two years.
