An LTS-to-LTS upgrade swaps out more than packages. Going from Ubuntu 24.04 to 26.04 replaces your sudo binary with the Rust rewrite sudo-rs, replaces most of GNU coreutils (ls, chmod, sort and the rest) with the Rust uutils port, moves the kernel from 6.8 to 7.0 and Python from 3.12 to 3.14. It also quietly disables every third-party repository on the box and doesn’t turn them back on. Know that before you start, not after a Docker update silently stops arriving.
We ran the upgrade on a 24.04 server carrying Docker CE from Docker’s own repo, a Launchpad PPA, Nginx, a Python virtual environment and a custom sudoers drop-in. The whole run took 13 minutes 44 seconds and changed 757 packages. Every command and output below comes from that machine.
Tested October 2026 upgrading Ubuntu 24.04.4 LTS to 26.04.1 LTS with third-party apt repos enabled.
Before You Start
- Ubuntu 24.04 LTS (Noble Numbat), fully updated
- A backup you have actually restored from once (VM snapshot, rsync, or whatever you trust)
- At least 5 GB free on
/. Our root filesystem grew by about 1.2 GB - Out-of-band console access (VNC, IPMI, or your cloud provider’s serial console) in case SSH drops mid-upgrade
- About 30 minutes, including the reboot and the post-upgrade checks
On a production server, run the upgrade on a clone first. Do not skip this. If you’re still on 22.04, upgrade to 24.04 first, then continue here. Ubuntu does not support skipping an LTS release.
If you run Python virtual environments, record what is installed in each one now. They break during the upgrade (details further down), and a requirements file makes the rebuild a one-liner:
/opt/appvenv/bin/pip freeze > ~/appvenv-requirements.txt
What Changes in the Upgrade
These are the versions on our test server before and after the upgrade:
| Component | Ubuntu 24.04 | Ubuntu 26.04 |
|---|---|---|
| Kernel | 6.8.0 | 7.0.0 |
| Python | 3.12.3 | 3.14.4 |
| sudo | sudo 1.9.15p5 (C) | sudo-rs 0.2.13 (Rust) |
| coreutils | GNU coreutils 9.4 | uutils coreutils 0.10.0 (Rust) |
| OpenSSH | 9.6p1 | 10.2p1 |
| systemd | 255 | 259 |
| OpenSSL | 3.0.13 | 3.5.5 |
| Codename | Noble Numbat | Resolute Raccoon |
For the feature-level view, see our Ubuntu 26.04 LTS features guide and the 26.04 vs 24.04 comparison.
Step 1: Fully Update Ubuntu 24.04
Bring the current release fully up to date first. full-upgrade also pulls in packages that a plain upgrade holds back:
sudo apt update && sudo apt full-upgrade -y
Then reboot if a kernel or core library was updated. The upgrade tool checks for this and refuses to continue while the flag file exists:
[ -f /var/run/reboot-required ] && sudo reboot
Our freshly patched server needed that reboot. Running the upgrade without it stops immediately:
Checking for a new Ubuntu release
You have not rebooted after updating a package which requires a reboot. Please reboot before upgrading.
“Please install all available updates for your release before upgrading”
do-release-upgrade refuses to start while any installed package still has a newer version waiting, and the only ones it ignores are phased updates. A package on hold is the usual cause. apt full-upgrade respects the hold and reports nothing left to do, but the upgrader still sees the newer version. This one-liner runs the same test the tool runs and prints the packages that block it:
python3 -c 'import apt; c=apt.Cache(); print([p.name for p in c if p.is_upgradable and "Phased-Update-Percentage" not in p.candidate.record])'
On our 24.04 test VM, putting nano on hold reproduced the error, and the one-liner named only that package. A phased cloud-init update was pending at the same time and didn’t count:
['nano']
Release the holds and run the full upgrade again. Once the one-liner prints an empty list, do-release-upgrade gets past this check:
apt-mark showhold
sudo apt-mark unhold $(apt-mark showhold)
sudo apt full-upgrade -y
An upgrade that failed partway blocks the check the same way, because the package never reached its new version. On the same VM a full root disk stopped snapd mid-unpack. Free some space, run sudo dpkg --configure -a, then repeat the full upgrade.
Step 2: Confirm the Upgrade Policy
The do-release-upgrade tool ships in update-manager-core. Server images include it, but install it explicitly on minimal builds:
sudo apt install -y update-manager-core
Check which releases the tool is allowed to offer:
grep -v '^#' /etc/update-manager/release-upgrades
The policy should read lts:
[DEFAULT]
Prompt=lts
If it says never, someone disabled release upgrades on purpose. Find out why before you change it to lts.
Step 3: Check That 26.04 Is Offered
Ask the tool whether a new LTS is available:
sudo do-release-upgrade -c
On 24.04 it now reports the point release:
Checking for a new Ubuntu release
New release '26.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
Canonical only publishes a new LTS to existing LTS users once the first point release ships. Ubuntu 26.04 came out on 23 April 2026, but 24.04 machines were not offered it until 26.04.1 landed in late August. Earlier guides, including the first version of this one, told you to force the upgrade with -d. Drop that flag now. The plain command upgrades you to the supported release.
“There is no development version of an LTS available”
This message means the tool found no newer LTS in meta-release-lts. A machine that’s already on 26.04 prints it on every check:
Checking for a new Ubuntu release
There is no development version of an LTS available.
To upgrade to the latest non-LTS development release
set Prompt=normal in /etc/update-manager/release-upgrades.
If you see it on 24.04, run lsb_release -ds first. A box that already upgraded prints this message too. Otherwise, confirm Prompt=lts from Step 2, update update-manager-core, and check that the server can reach changelogs.ubuntu.com over HTTPS. Don’t switch to Prompt=normal to make it go away. That moves the server onto the nine-month interim release track.
Step 4: Run the Upgrade
Start it from a tmux or screen session so a dropped SSH connection does not kill it. Over SSH, the interactive frontend also offers to start a backup sshd on port 1022. It only helps if that port is reachable, so open it in ufw or your cloud security group before you start:
sudo do-release-upgrade
The tool works through these stages:
- Disables every third-party repository and PPA by adding
Enabled: noto its.sourcesfile - Rewrites
ubuntu.sourcesfromnobletoresolute - Calculates the upgrade (757 package changes on our server: 643 upgrades and 114 new packages) and asks you to confirm
- Downloads and installs everything, asking about config files you have modified (keep your version unless you know the new default is what you want)
- Removes obsolete packages and prompts for the reboot
It does not re-enable the repositories it switched off, even when they already publish packages for 26.04. Docker and the git-core PPA both had resolute builds available, and both stayed disabled. The post-upgrade section below shows how to bring them back.
On our test server (4 vCPUs, 4 GB RAM), the download phase took about 5 minutes and the install about 8. The whole run took 13 minutes 44 seconds.
For automation, the non-interactive frontend answers every prompt itself and keeps your existing config files on conflicts. That is the mode our test used:
sudo do-release-upgrade -f DistUpgradeViewNonInteractive
Use it only on a server whose upgrade you have already rehearsed on a clone. It will not stop to ask about anything.
Step 5: Reboot
Reboot into the 7.0 kernel:
sudo reboot
Physical servers with a long firmware POST take a few minutes to come back. Give them that time before you reach for the console.
Step 6: Verify the Upgrade
Confirm the release:
lsb_release -a
It should name the 26.04 point release, not a development branch:
Distributor ID: Ubuntu
Description: Ubuntu 26.04.1 LTS
Release: 26.04
Codename: resolute
Check the kernel, the toolchain swaps and failed units in one pass:
uname -r; python3 --version; sudo --version | head -1; ls --version | head -1; ssh -V; systemctl --failed --no-legend | wc -l
Every component reports its 26.04 version, and no systemd unit failed:

Then check that your services came back. On our box, nginx, docker and containerd were all active, and containers still ran. They were still running the 24.04 Docker packages at this point, because Docker’s repository was disabled.
Re-enable Third-Party Repositories
Find the repositories the upgrade switched off:
grep -l '^Enabled: no' /etc/apt/sources.list.d/*.sources
Both third-party sources on our server were listed:
/etc/apt/sources.list.d/docker.sources
/etc/apt/sources.list.d/git-core-ubuntu-ppa-noble.sources
Repos still in the one-line .list format do not get Enabled: no. The upgrader comments their lines out instead, so open any .list files in /etc/apt/sources.list.d/ and check them by eye too.
Before re-enabling one, confirm the vendor publishes a resolute suite. Open <repo URL>/dists/resolute/Release in a browser or with curl -I. A 404 means the vendor has no 26.04 build yet, and pointing the repo at resolute will break apt update. Docker and the git-core PPA both returned 200, so we pointed them at resolute and removed the disable line:
sudo sed -i -e 's/^Suites: noble$/Suites: resolute/' -e '/^Enabled: no$/d' /etc/apt/sources.list.d/docker.sources /etc/apt/sources.list.d/git-core-ubuntu-ppa-noble.sources
Refresh the package lists and look at what changed:
sudo apt update && apt list --upgradable
Every Docker and git package now has a 26.04 build waiting to replace the 24.04 one:
containerd.io/resolute 2.3.6-1~ubuntu.26.04~resolute amd64 [upgradable from: 2.3.6-1~ubuntu.24.04~noble]
docker-buildx-plugin/resolute 0.37.1-1~ubuntu.26.04~resolute amd64 [upgradable from: 0.37.1-1~ubuntu.24.04~noble]
docker-ce-cli/resolute 5:29.8.2-1~ubuntu.26.04~resolute amd64 [upgradable from: 5:29.8.2-1~ubuntu.24.04~noble]
docker-ce-rootless-extras/resolute 5:29.8.2-1~ubuntu.26.04~resolute amd64 [upgradable from: 5:29.8.2-1~ubuntu.24.04~noble]
docker-ce/resolute 5:29.8.2-1~ubuntu.26.04~resolute amd64 [upgradable from: 5:29.8.2-1~ubuntu.24.04~noble]
docker-compose-plugin/resolute 5.6.0-1~ubuntu.26.04~resolute amd64 [upgradable from: 5.6.0-1~ubuntu.24.04~noble]
git-man/resolute 1:2.55.0-0ppa1~ubuntu26.04.2 all [upgradable from: 1:2.55.0-0ppa1~ubuntu24.04.2]
git/resolute 1:2.55.0-0ppa1~ubuntu26.04.2 amd64 [upgradable from: 1:2.55.0-0ppa1~ubuntu24.04.2]
Install them with sudo apt full-upgrade -y. A repository left disabled does not break anything on day one. The failure mode is slower: that software never gets another security update. For a fresh Docker setup on the new release, follow installing Docker CE on Ubuntu 26.04.
Finally, remove packages the new release no longer needs and check disk usage:
sudo apt autoremove -y --purge && df -h /
Usage on our 29 GB root went from 2.8 GB to 4.0 GB:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 29G 4.0G 25G 14% /
The old 6.8 kernel stays installed as a GRUB fallback. Leave it until the server has run cleanly on 7.0 for a while.
What Breaks After Upgrading
Python virtual environments stop finding their packages
A venv’s interpreter is a symlink to /usr/bin/python3, which is now 3.14, while its packages sit in lib/python3.12/site-packages. The venv still starts, but every third-party import fails. Our test venv did exactly that:
Traceback (most recent call last):
File "<string>", line 1, in <module>
import requests
ModuleNotFoundError: No module named 'requests'
Recreate the venv and reinstall from the requirements file you saved before the upgrade. Our venv lives in root-owned /opt, hence the sudo. Run both commands as whichever user owns the venv, or a service account’s environment ends up owned by root:
sudo python3 -m venv --clear /opt/appvenv && sudo /opt/appvenv/bin/pip install -r ~/appvenv-requirements.txt
--clear wipes the old environment. Without the requirements file you are rebuilding the package list from memory. See Python on Ubuntu 26.04 for the venv and pip changes in the new release.
sudo is now sudo-rs
The command is still called sudo, and our sudoers drop-in kept working unchanged (sudo -l -U deploy listed the same rule). Piping a password with sudo -S also worked. Test anything that depends on unusual sudoers features, such as plugins or complex Defaults lines, before you trust it in production. If something breaks, the original C sudo is still installed and you can switch back through alternatives:
sudo update-alternatives --set sudo /usr/bin/sudo.ws
The alternatives system confirms the switch, and sudo --version then reports Sudo version 1.9.17p2:
update-alternatives: using /usr/bin/sudo.ws to provide /usr/bin/sudo (sudo) in manual mode
Return to sudo-rs with sudo update-alternatives --auto sudo. Our sudo-rs guide covers the differences in detail.
ls and most coreutils are the Rust uutils versions
The upgrade installs coreutils-from-uutils, so /usr/bin/ls now resolves to /usr/lib/cargo/bin/coreutils/ls. Ubuntu kept cp, mv and rm on GNU, so /usr/bin/cp links to gnucp and still reports GNU coreutils 9.7. Shell scripts that parse coreutils output or rely on obscure GNU flags are where differences show up. If one breaks, swap GNU coreutils back in. coreutils-from-uutils is a protected package, so apt needs the explicit flag to remove it:
sudo apt install --allow-remove-essential coreutils-from-gnu coreutils-from-uutils-
After that, ls --version reports ls (GNU coreutils) 9.7. The same command with the two package names swapped returns you to uutils. More on what differs in our Rust coreutils guide for Ubuntu 26.04.
OpenSSH 10 drops DSA keys
OpenSSH 10.0 removed DSA support completely. Any user or automation still authenticating with an ssh-dss key is locked out after the upgrade. Grep authorized_keys files for ssh-dss before you upgrade, and replace those keys with Ed25519. Our SSH server configuration guide covers the relevant sshd_config settings, and journalctl filtering helps when you need to find the rejected logins in the systemd 259 journal.
Can I upgrade directly from Ubuntu 22.04 to 26.04?
No. Ubuntu requires sequential LTS upgrades: 22.04 to 24.04 first, then 24.04 to 26.04. Reboot and verify between the two hops. Our 24.04 to 26.04 hop took under 14 minutes, so with both reboots and checks, budget about an hour for the full path.
Is it safe to upgrade a production server?
Rehearse it on a clone first. The base system upgraded cleanly on our test server with zero failed units. The risk sits in your stack: Python modules built for 3.12, scripts that hit coreutils or sudo edge cases, and third-party repos left disabled. Once the upgrade succeeds, work through the Ubuntu 26.04 server post-install checklist and the server hardening guide.
What if the upgrade fails halfway through?
Reconnect (through the backup sshd on port 1022 if you opened it and the main sshd is down) and finish the interrupted package configuration:
sudo dpkg --configure -a && sudo apt -f install
Read /var/log/dist-upgrade/main.log and apt.log to see where it stopped. If the system will not boot, pick the old 6.8 kernel from the GRUB menu, or restore the backup you took before Step 1. Cloud images hide the GRUB menu, so you need the out-of-band console to reach it, and the old kernel is only a recovery path, not somewhere to stay.
Thank you very much for this detailed instruction how to upgrade Ubuntus LTS version. Worked ! I hopped in two steps from Ubuntu 22.04 to 26.04 using the server version.
Great, two-step LTS-to-LTS hops are the safest path. 22.04 to 24.04 first, then 24.04 to 26.04, because do-release-upgrade only supports one major jump at a time and the intermediate reboot lets you catch any kernel or broken-package issues before stacking another upgrade on top.
Thanks for the instructions but doesn’t work on LTS release:
~$ sudo do-release-upgrade
Checking for a new Ubuntu release
There is no development version of an LTS available.
????
use -d. i had the same issue, but the -d helped
Add the “-d” development flag if you want to upgrade
sudo do-release-upgrade -d
Thanks Andy. The article already covers the -d flag in Step 3 (still required until 26.04.1 ships, around July 2026). Good catch for anyone skimming.
I consider myself an intermediate level user. That’s halfway between novice and professional. So my art club thinks I’m a nerd. While the guys who maintain servers considered me a dunce!
Anyway, I appreciated all the help here. This took the stress out of what appeared to be a very long-winded list of automated actions!
Glad it took the stress out. The guide is now updated for 26.04.1, so plain sudo do-release-upgrade offers the upgrade without the -d flag.
The note about third-party repos being quietly disabled is the part that would have caught me out. I run a couple of self-hosted boxes with Docker’s repo and a PPA, and I would have assumed they carry over. Did the move to uutils break any of your existing shell scripts, mainly the ones relying on GNU-specific flags for cp or ls?
cp didn’t change for us. Ubuntu redirected cp, mv and rm back to GNU before release, so on 26.04
cp --versionstill reports GNU coreutils 9.7. ls is uutils 0.10.0, and the GNU-only ls flags we tried (--time-style=long-iso,--group-directories-first,--sort=version,--quoting-style,--hide,--dired) all behaved the same. If a script does trip on a uutils tool, the GNU build of every utility is still installed with a gnu prefix, so you can point it atgnuls,gnudateorgnusortwithout touching the rest of the system.Running ubuntu studio 24.0.4, tried everything I can think of to update and remove any outside repos, still getting “Please install all available updates for your release before upgrading.”
That check fails while any package that isn’t a phased update still has a newer version waiting. A held package is the usual cause, because apt full-upgrade skips holds and reports nothing to do. This prints exactly what the upgrader is tripping on:
python3 -c 'import apt; c=apt.Cache(); print([p.name for p in c if p.is_upgradable and "Phased-Update-Percentage" not in p.candidate.record])'If it lists anything, check
apt-mark showhold, unhold those packages withsudo apt-mark unhold, then runsudo apt full-upgradeagain. We added a section for this error under Step 1 with the details.Thanks for your reply,
the python command returns:
[‘distro-info-data’, ‘ubuntustudio-audio-core’]
I get nothing from apt-mark showhold