I upgraded an Ubuntu 26.04 LTS server to 26.10 four days after Stonking Stingray shipped, and the first surprise was that Ubuntu wouldn’t offer it. Two weeks after release I ran it again, expecting the normal prompt to work by then. It didn’t. If you want to upgrade Ubuntu 26.04 to 26.10 right now, you still have to ask for it with the -d flag, and that changes how careful you should be.
My notes run from the release check to a snapshot rollback. Each run took about 13 minutes after I confirmed. On the way I hit a “Foreign Packages” warning, PPAs and a Docker repo switched off silently, a PPA with no 26.10 build, and SSH refusing logins for three minutes. I ran the upgrade twice in October 2026 on Ubuntu 26.04.1 LTS server VMs in my lab, one with nginx and Docker CE and one with the git-core and deadsnakes PPAs, and every prompt and log line below comes from those boxes.
Deciding whether to leave the LTS
An interim release gets nine months of support, so moving a server from 26.04 LTS to 26.10 means you’ll be upgrading again before July 2027. That is fine for a test box or a laptop where you want the newer kernel and toolchain, but it’s a bad trade for anything you set up once and forget. I would hold off in these cases:
- Production servers: Ubuntu’s own server docs say the
-dpath isn’t recommended for production - Machines with TPM-backed full disk encryption: the release notes say these can’t upgrade to 26.10 at release
- Anything that has to stay supported past mid-2027 without another upgrade
If you’re still on 24.04, do the LTS hop first with our Ubuntu 24.04 to 26.04 upgrade guide, because the upgrader can’t skip past the next LTS, so 24.04 can’t go straight to 26.10. For a fresh machine, the Ubuntu 26.10 install walkthrough saves you the cleanup I describe below.
Getting 26.04 fully patched first
The upgrader refuses to start on a system that’s behind on updates or still waiting for a reboot, so I patched everything first. Ubuntu’s server documentation adds the phased-updates option, because packages that are still rolling out gradually can block the release upgrade and need to be installed first:
sudo apt update
sudo apt full-upgrade -o APT::Get::Always-Include-Phased-Updates=true
While you’re here, check for /lib32 and /libx32 symlinks. The release notes list a known issue where the upgrade can stop while installing base-files on systems where /lib32 is a symlink pointing at nothing (the bug report covers the other multilib links too), and say the impact on this upgrade path isn’t clear yet:
ls -ld /lib32 /libx32
My box answered “No such file or directory” for both, which is the safe result. If yours shows a symlink whose target is missing, read Launchpad bug 2131303 before you go on.
Take a snapshot or a backup now if the machine is a VM or holds anything you care about, because it’s your only way back. Ubuntu has no downgrade path: do-release-upgrade only moves forward, and once the packages are installed, there’s no supported way to put 26.04 back over them. The rollback section at the end shows what restoring my Proxmox snapshot gave back.
“You have not rebooted after updating a package which requires a reboot”
My first attempt failed right here. The patch run had installed a new kernel and libc6, and do-release-upgrade stopped before it downloaded anything:
Checking for a new Ubuntu release
You have not rebooted after updating a package which requires a reboot. Please reboot before upgrading.
On my box, /var/run/reboot-required.pkgs named the packages behind that block: libc6, linux-base and the new kernel image. A reboot clears it, so reboot and log back in before you go on:
sudo reboot
After the reboot, ls /var/run/reboot-required should report that the file doesn’t exist, and that’s your signal the upgrader will start.
Switching the upgrade prompt from lts to normal
Every 26.04 install ships with Prompt=lts in /etc/update-manager/release-upgrades, which tells the upgrader to wait for 28.04 and ignore everything in between. Print the file without its comments to see what yours says:
grep -v '^#' /etc/update-manager/release-upgrades | grep .
A default install answers with the LTS-only setting:
[DEFAULT]
Prompt=lts
Flip it to normal so interim releases count. On a desktop, the same switch is the “Notify me of a new Ubuntu version” menu under the Updates tab of Software & Updates, set to “For any new version”, but I only tested the file on this server:
sudo sed -i 's/^Prompt=.*/Prompt=normal/' /etc/update-manager/release-upgrades
Then ask the upgrader what it can see, once through the normal feed and once through the development feed that -d reads. The -c flag only checks and changes nothing:
do-release-upgrade -c
do-release-upgrade -d -c
My box gave this sequence. Before the change, both checks gave the LTS message. After it, the normal feed found nothing and only the development feed offered 26.10:

That “No new release found” isn’t an error on your side. Canonical publishes upgrade paths in changelogs.ubuntu.com/meta-release, and 26.10 still isn’t in it. Ubuntu’s desktop upgrade docs say interim upgrades open a few days after the release date, so I expected the normal prompt to work by the time I ran the upgrade again. On October 29, 14 days after release, the file still ended at 26.04.1 LTS. 26.10 was still listed only in meta-release-development, as “Supported: 0”:
curl -s https://changelogs.ubuntu.com/meta-release | grep -E "^(Dist|Version|Supported):" | tail -3
Running it from the same box returned the LTS entry as the newest release:
Dist: resolute
Version: 26.04.1 LTS
Supported: 1
That curl is the quickest way to know when you can drop -d. When the last block says Dist: stonking, plain sudo do-release-upgrade will offer 26.10 with Prompt=normal, and every other step on this page stays the same. Until then you choose between waiting and the development feed, and Ubuntu’s own server docs don’t recommend the development feed for production.
“There is no development version of an LTS available”
Why would a plain -c check talk about a development version? I got the message without -d too, and it simply means the Prompt line still says lts, and the last two lines of the message tell you the fix: set Prompt=normal and check again.
Running do-release-upgrade inside tmux
I ran the upgrade inside tmux over SSH, so a dropped connection would’ve left the upgrader running instead of killing it halfway through package configuration. If you haven’t used it before, our tmux cheat sheet covers detaching and reattaching. Start a session, then the upgrade:
tmux new -s upgrade
sudo do-release-upgrade -d
If your SSH session drops, log back in and reattach with tmux attach -t upgrade. The upgrader then asked me five questions, and these are the answers I gave, in order:
- Release announcement, Continue:
y - Foreign Packages Installed, continue anyway:
y, after checking them (next section) - Start the upgrade:
dto read the list,qto leave it, theny - Remove obsolete packages:
y, which removed 30 packages - Restart required:
y
Run it straight from an SSH shell instead of tmux and you’ll likely get one more question, right after the release announcement. The upgrader’s code checks whether it’s a child of sshd, and if it is, it asks “Continue running under SSH?” and starts a spare SSH daemon on port 1022. Under tmux its parent is the tmux server, which is why I never saw that prompt. It also wraps itself in GNU screen, and /var/log/dist-upgrade/screenlog.0 is the full transcript of that session.
No config file question came up, though I’d expect that on a server with an edited sshd_config or nginx config. If it does ask, keep your version unless you know the new default is what you want, then compare the two later.
“The following unofficial packages are currently installed”
This prompt made me stop and check, because the three “unofficial” packages are stock Ubuntu libraries that I never touched:

With the prompt still waiting, I opened a second shell and compared versions. All three are 26.04 security updates with an ubuntu0.1 suffix, and they’re newer than the builds in the 26.10 release pocket. The installed libx11-6 is a 26.04 security update for four CVEs, the 26.10 build has a lower version number, and stonking-security had nothing newer that day. The upgrader can’t find the installed versions in any 26.10 source, so it calls them unofficial:
apt-cache policy libx11-6
After the upgrade, the policy output shows the newer 26.04 security build still installed, with the 26.10 build sitting below it as a lower version:
libx11-6:
Installed: 2:1.8.13-1ubuntu0.1
Candidate: 2:1.8.13-1ubuntu0.1
Version table:
*** 2:1.8.13-1ubuntu0.1 100
100 /var/lib/dpkg/status
2:1.8.13-1 500
500 http://archive.ubuntu.com/ubuntu stonking/main amd64 Packages
Answering y is safe here. apt does not downgrade, so you keep the patched library until stonking-security publishes its own fix, and that build then replaces it on a normal apt upgrade. If your list shows a package you really did install from a PPA or a .deb, that’s a different case, and it’s worth reading the warning literally.
My second box had exactly that case. Before the upgrade I installed git from ppa:git-core/ppa and python3.13 from ppa:deadsnakes/ppa, and the same prompt now opened with the PPA packages:
The following unofficial packages are currently installed:
git......................Installed from: LP-PPA-git-core
git-man..................Installed from: LP-PPA-git-core
libbz2-1.0...............Installed from: unknown origin
libx11-6.................Installed from: unknown origin
libx11-data..............Installed from: unknown origin
Python 3.13 wasn’t on that list, even though it came from a PPA too. main.log shows where the two split. Before the upgrader rewrote the sources, its foreign list was git git-man libpython3.13-stdlib python3.13. After the rewrite, only the git packages and the three libraries were left. The difference, as far as I can tell, is that 26.10 ships its own git (2.55.0 in stonking) to compare against, but no python3.13 at all, so there was nothing to call it foreign against. Later in the same log it shows up only as skipping foreign pkg 'python3.13'. Either way, I answered y. Neither PPA package was removed or downgraded: both were still at their 26.04 PPA builds after the reboot, which is what you want for a tool your scripts depend on.
What the change summary means for SSH
The summary on my box read “1 package is going to be removed. 64 new packages are going to be installed. 502 packages are going to be upgraded”, with 1326 MB to download. The one removal was openssh-client. Removing your SSH client in the middle of an SSH session sounds alarming, but the upgrader’s log in /var/log/dist-upgrade/main.log explains it. 26.10 splits OpenSSH into a plain build and a GSSAPI/Kerberos build, and the default /etc/ssh/ssh_config on my box has GSSAPIAuthentication yes under Host *, so the upgrader swapped in openssh-client-gssapi:
2026-10-19 13:50:27,506 DEBUG found GSSAPI/Kerberos option in /etc/ssh/ssh_config: GSSAPIAuthentication yes
2026-10-19 13:50:27,506 DEBUG GSSAPI usage detected, switching openssh-client to openssh-client-gssapi
Only the client package was swapped. openssh-server stayed installed, and ssh came back active after the reboot. The real catch with that swap only showed up later, in the cleanup section below.
“ssh: connect to host box1 port 22: Connection refused” in the middle of the upgrade
On the second run, I was polling the box from another terminal and suddenly every new SSH connection was refused. Had the OpenSSH swap broken the server? No: the tmux session kept running and the upgrade went on configuring packages. The journal shows what happened. While the new OpenSSH packages were being configured, the server was simply stopped for almost three minutes:
2026-10-29T00:21:56+00:00 box1 systemd[1]: Stopped ssh.service - OpenBSD Secure Shell server.
2026-10-29T00:24:49+00:00 box1 systemd[1]: Listening on ssh.socket - OpenBSD Secure Shell server socket.
2026-10-29T00:24:50+00:00 box1 sshd[22645]: Server listening on 0.0.0.0 port 22.
The upgrade never noticed, because it was running in tmux, and the tmux server doesn’t depend on the SSH service. Ubuntu’s ssh.service also sets KillMode=process, so stopping it should leave sessions that are already open alone, but I didn’t have a plain SSH session attached during that window to prove it. New logins definitely fail, so if your connection drops around then, wait a few minutes before you assume the box is gone. Once port 22 answers again, reattach to tmux. This is also the moment when a hypervisor or serial console pays off.
Both runs printed “System upgrade is complete” about 13 minutes after I confirmed the start. Say y to the restart prompt, then reconnect when the box comes back.
Checking what changed after the reboot
Did the box really land on 26.10, and did my two services come back on their own? These checks cover the release, the kernel and the services in one pass:
lsb_release -ds; uname -r
openssl version
readlink -f /usr/bin/cp /usr/bin/mv /usr/bin/rm
systemctl is-active dbus-broker dbus nginx docker ssh
sudo docker ps --format "{{.Names}} {{.Status}}"
systemctl --failed --no-legend
All five units reported active, systemctl --failed printed nothing, and the nginx container answered on port 8080 without me touching it. The cp, mv and rm commands now point at the Rust uutils binaries, and dbus-broker has taken over as the message bus:

I ran the same version checks before and after, and the table shows what moved under the services. On 26.04, cp resolved to /usr/bin/gnucp (the GNU build), and our Rust coreutils guide shows how to switch back if a script of yours depends on a GNU-only flag. Anything that links against OpenSSL or uses its old engine interface deserves a test, because OpenSSL 4 drops engines entirely.
| Component | 26.04.1 LTS (before) | 26.10 (after) |
|---|---|---|
| Kernel | 7.0.0-38-generic | 7.3.0-9-generic |
| systemd | 259.5 | 261.3 |
| OpenSSL | 3.5.5 | 4.0.3 |
| OpenSSH | 10.2p1 | 10.5p1 (GSSAPI client) |
| Python | 3.14.4 | 3.14.8 |
| sudo-rs | 0.2.13 | 0.2.14 |
| cp | GNU (gnucp) | uutils |
| nginx | 1.28.3 | 1.30.4 |
| apt | 3.2.0 | 3.3.3 |
| Installed packages | 702 | 735 |
| Root disk used | 3.4 GB | 4.4 GB |
The extra gigabyte surprised me on a VM. The new linux-generic pulls in the split linux-firmware packages, and /usr/lib/firmware grew to 792 MB on a machine with no real hardware to load firmware into. linux-image-generic depends on linux-firmware or linux-firmware-minimal, so I left it alone. I haven’t tested swapping to the minimal package on a VM.
Finish with a normal update run, because the upgrader doesn’t install everything that’s already in the security pocket. Right after my reboot, four OpenSSH packages were already waiting in stonking-security:
sudo apt update
sudo apt full-upgrade
That run pulled the newer OpenSSH server and client from stonking-security, and /var/run/reboot-required didn’t come back afterwards. On the October 29 box, the upgrade had already installed that OpenSSH build, but ten packages from the 7.3.0-10 kernel update were waiting in stonking-updates. Expect that list to change from week to week, so run the update every time.
Turning PPAs and third-party repos back on
The upgrader disables every third-party source without asking. On both boxes the only trace was a line in main.log for each source, saying the entry “was disabled (unknown mirror)”. Ubuntu’s server docs say the same thing more politely: PPAs and third-party repositories are switched off during a release upgrade, and nothing installed from them is removed. The upgrader writes Enabled: no into each .sources file and leaves Suites: resolute as it was, so those packages stay on their 26.04 builds and get no more updates. Find the sources it switched off:
grep -l "Enabled: no" /etc/apt/sources.list.d/*.sources
Before you turn one back on, check that it actually publishes a 26.10 suite. A PPA either has a dists/stonking directory or it doesn’t, so a single curl per PPA answers that. The git-core PPA returned 200 and deadsnakes returned 404:
curl -s -o /dev/null -w "%{http_code}\n" https://ppa.launchpadcontent.net/git-core/ppa/ubuntu/dists/stonking/Release
curl -s -o /dev/null -w "%{http_code}\n" https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu/dists/stonking/Release
For a PPA, I’d rather run add-apt-repository again than edit the old file. On 26.10 it writes a new git-core-ubuntu-ppa-stonking.sources next to the disabled resolute one:
sudo add-apt-repository -y ppa:git-core/ppa
I ran the same command for deadsnakes anyway, to see how a PPA without a 26.10 suite fails. The capture leaves out the long PPA descriptions that add-apt-repository prints:

The git-core suite answered with a normal Hit line, while deadsnakes stopped apt update with an error.
“The repository ‘https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu stonking Release’ does not have a Release file”
A 404 on the Release file means the PPA has no build for that suite. For deadsnakes, that won’t change: its own description lists only the LTS releases (22.04, 24.04 and 26.04) as supported, so there’s no point waiting for a 26.10 build. Pointing the file back at resolute silences it, but then you’re installing 26.04 builds on 26.10, and that’s the kind of mix that breaks the next upgrade. I removed the PPA and deleted both disabled resolute files, so apt update runs clean again:
sudo add-apt-repository -y --remove ppa:deadsnakes/ppa
sudo rm /etc/apt/sources.list.d/git-core-ubuntu-ppa-resolute.sources /etc/apt/sources.list.d/deadsnakes-ubuntu-ppa-resolute.sources
sudo apt update
sudo apt full-upgrade
That full-upgrade moved git from 1:2.56.0-0ppa1~ubuntu26.04.2 to the PPA’s ~ubuntu26.10.2 build. Python 3.13 is now an orphan, and apt list '?obsolete' shows it as installed with no source (trimmed to the Python lines, the rest were old 7.0 kernel packages):
libpython3.13-stdlib/now 3.13.18-1+resolute1 amd64 [installed,local]
python3.13/now 3.13.18-1+resolute1 amd64 [installed,local]
It still runs. It links against libssl.so.3, and 26.10 still ships libssl3t64 3.5.5 alongside OpenSSL 4, so import ssl worked. It won’t get security fixes, though, and nothing guarantees that library stays in 26.10. I would move whatever needs 3.13 to an interpreter managed by uv or pyenv (our Python on Ubuntu 26.04 guide covers both), then sudo apt purge python3.13 libpython3.13-stdlib. The old 7.0 kernel packages in that list are a different case: a dry run with apt-get -s autoremove showed it would remove all eight.
Vendor repos work the same way. On the first box Docker already had stonking on download.docker.com, so I pointed its file at that suite and removed the disable line:
sudo sed -i -e 's/^Suites: resolute$/Suites: stonking/' -e '/^Enabled: no$/d' /etc/apt/sources.list.d/docker.sources
sudo apt update
sudo apt full-upgrade
Docker CE, containerd and the plugins moved to their ~ubuntu.26.10~stonking builds, and the container was serving again within a minute. If you installed Docker the way our Docker CE on Ubuntu 26.04 guide does, the same two sed expressions work on your file. For a vendor without a 26.10 suite yet, leave it disabled rather than pointing it at resolute, because mixing release builds is how upgrades break months later.
Cleaning up the leftover openssh-client package
After the reboot, dpkg -l still listed the old SSH client in the rc state, meaning the package is gone but its config is still registered:
dpkg -l | grep '^rc'
It was the only one left behind:
rc openssh-client 1:10.2p1-2ubuntu3.8 amd64 secure shell (SSH) client, for secure access to remote machines
The Ubuntu 26.10 release notes list this as a known issue: purging an rc OpenSSH package breaks the OpenSSH configuration, remote access may be lost, and the fix for the plain openssh packages is due as an update. I wanted to know what “breaks” meant, so I purged it on snapshots three times: straight after the upgrade, again with DH group exchange switched back on, and once more after apt full-upgrade had installed the newer OpenSSH from stonking-security. Every run deleted the same file, and the first two, from before the update, showed this:
/etc/ssh/modulideleted, although dpkg lists it as anopenssh-serverfilesshd -tstill passed and new key logins kept working- Default settings: no visible change, since sshd here doesn’t offer DH group exchange anyway
- DH group exchange enabled for an old client: it connected, but sshd fell back to a fixed modulus
The purge also printed warn: The group `ssh' does not exist., which is harmless here because the agent’s group on 26.10 is _ssh. Before trusting that “no visible change”, I rolled back and tested a client that offers only diffie-hellman-group-exchange-sha256 against the untouched server. That control run was refused too, so the refusal comes from the default key exchange list, not from the purge. That’s why I don’t claim more damage than the missing file.
“WARNING: could not open /etc/ssh/moduli (No such file or directory), using fixed modulus”
You’ll see this in journalctl -u ssh after the purge, but only if you’ve added DH group exchange back to KexAlgorithms for an old client. The connection still works, but sshd uses a built-in modulus instead of the moduli list. Plain apt install --reinstall openssh-server doesn’t bring the file back, because dpkg won’t recreate a conffile you deleted. Telling dpkg to restore missing conffiles does:
sudo apt install --reinstall -o Dpkg::Options::=--force-confmiss openssh-server
The reinstall prints “Configuration file ‘/etc/ssh/moduli’, does not exist on system” and then puts it back. Check the file, then open a second SSH login from another terminal before you close this one, because that’s the real proof remote access still works:
ls -l /etc/ssh/moduli
I made the mistake of running sudo sshd -t straight after the reinstall, and it failed with “Missing privilege separation directory: /run/sshd”. It looked like a broken server, but it wasn’t. SSH is socket-activated here, and /run/sshd only came back with my next login (Launchpad bug 1991283 describes the same message). Once the second login worked, the config test passed:
sudo sshd -t && echo "sshd config OK"
My advice is simpler than the repair: do not purge any OpenSSH package that dpkg -l lists as rc, and don’t run a blanket apt purge '~c' on this machine until Ubuntu ships the fix for the plain OpenSSH packages. Only the client was swapped on my box. The release notes cover the server packages too, though, and Launchpad bug 2169738 describes purging openssh-server after openssh-server-gssapi goes in as rendering SSH unusable. Watch that bug for the fix. An rc entry costs you nothing. If you’re hardening SSH on the new release anyway, our post-quantum SSH hardening guide covers the key exchange settings, written for 26.04.
Rolling back to 26.04 from a snapshot
Restoring a snapshot is the only rollback I’d trust here. Once the box is on 26.10, its libc6, systemd and coreutils all come from the newer release, and apt won’t take you backwards. My second box ran on zfs-pool storage in Proxmox, and I took the snapshot after the PPAs went in and the box had rebooted onto the patched kernel, just before do-release-upgrade -d. I did both the snapshot and the rollback through the Proxmox API. On the node, the same two steps with the CLI look like this (replace 101 with your VM ID):
qm snapshot 101 pre2610 --description "26.04.1 patched, before do-release-upgrade"
qm rollback 101 pre2610 --start 1
The rollback task finished in about five seconds, and SSH answered 18 seconds after I started it. Back in the box, these checks show the release, the kernel, the PPA packages and the sources:
lsb_release -ds; uname -r
git --version; python3.13 --version
grep -H -E "^(Suites|Enabled)" /etc/apt/sources.list.d/*ppa*.sources
grep "^Prompt" /etc/update-manager/release-upgrades
Everything came back exactly as it was at snapshot time:
Ubuntu 26.04.1 LTS
7.0.0-42-generic
git version 2.56.0
Python 3.13.18
/etc/apt/sources.list.d/deadsnakes-ubuntu-ppa-resolute.sources:Suites: resolute
/etc/apt/sources.list.d/git-core-ubuntu-ppa-resolute.sources:Suites: resolute
Prompt=normal
Both PPA files are enabled again, and /var/log/dist-upgrade/ is empty. Note the last line, though. I took this snapshot after switching to Prompt=normal, so the rollback kept it, and an unattended check on this box would still find 26.10 the moment it reaches the normal feed. If you snapshot in the order this page gives, before the Prompt change, you get Prompt=lts back instead. If yours says normal and you’re rolling back to stay on the LTS, set it back to lts.
One trap on ZFS-backed storage, which is what my box used: Proxmox can only roll back to the most recent snapshot there (LVM-thin storage doesn’t have that limit). If you take a “postupgrade” snapshot after pre2610, delete it before you roll back, or the rollback fails with “is not most recent snapshot”.
Remember too that a rollback throws away everything written after the snapshot, including logs and database rows. On a busy server, stop the services that write data before you take the snapshot, or copy their data out before you roll back. On bare metal without snapshots, your backup is the rollback. I have not tested an LVM or Btrfs snapshot on this upgrade, so I cannot vouch for either one. If you’re building the hypervisor side from scratch, our Proxmox VE 9 install guide sets up the storage these snapshots live on.
What I still don’t know
This was two server VMs, so a few questions stayed open, and I’d rather list them than guess:
- Desktop path: I didn’t run the graphical upgrade from Software Updater
- TPM-backed encryption: blocked at release, and I have no date for when that lifts
- Normal feed: Ubuntu says a few days after release, but still closed on October 29
- Other PPAs: git-core had a 26.10 build and LTS-only deadsnakes didn’t, but I only tested those two
- Server package purge: my box never had
openssh-serverinrc, so I couldn’t reproduce bug 2169738
The last one bothers me most, because my default server survived the client purge and the bug report describes a much worse outcome for the server package. If you depend on SSH to reach the machine, keep a console path (a hypervisor console, IPMI or a cloud serial console) open before any purge until the OpenSSH update lands. I’ll rerun the purge test when it does. The Foreign Packages warning, meanwhile, looked the same on both runs: the same three Ubuntu libraries, plus whatever you installed from PPAs. When the curl from the prompt section finally prints Dist: stonking, I’ll run the upgrade a third time without -d to see whether the normal feed changes any of this.