中文 English

One sentence let Claude upgrade Ubuntu 22.04 to 24.04: a near-zero-screen-time cross-LTS run

Published: 2026-06-19
Ubuntu LTS-22 LTS-24 LTS do-release-upgrade systemd tmux sshd AI Agent Claude self-hosting home-lab troubleshooting

TL;DR

A remote home-lab box had been sitting on Ubuntu 22.04.4 LTS (jammy, kernel 5.15) for a long time. After one sentence — “upgrade the box at 192.168.103.182 to Ubuntu 24.04” — Claude opened a tmux session to babysit the run, brought up a fallback sshd on port 1022 so a mid-upgrade sshd restart would not strand me, then drove do-release-upgrade -f DistUpgradeViewNonInteractive end to end. Twenty-five minutes later the host came back: kernel 6.8.0-124, every service still listening.

This is not a tutorial on do-release-upgrade; the Ubuntu docs already cover that. This is about what an Agent does once it gets an SSH handle on a box, why it does each step, and which pitfalls you have to clear out of the way before letting it loose.

Ubuntu 24.04 LTS default desktop (public screenshot from Wikimedia Commons)

Cover: the official Ubuntu 24.04 LTS (Noble Numbat) desktop. This is what the upgraded room looks like.


1. Why I handed the upgrade to an Agent

A remote machine at home does two things for me around the clock: runs v2ray, and runs a self-written Python SOCKS5/HTTP proxy. Every device in the house tunnels through it. It does not get rebooted unless I am the one doing it.

Ubuntu 22.04 (jammy) had been on duty since 2022 — default kernel 5.15, Python 3.10. I was used to it. But 24.04 (noble) had been out for a year and a half; its kernel is 6.8, its systemd is 255, its Python is 3.12. Better a short pain now than a long one later — I would miss the next LTS window if I kept procrastinating.

The problem: the box is not on my desk. It lives in a weak-current cabinet wired into a PVE host, and the only way in is SSH. Remote-upgrading Ubuntu has three specific pain points for me:

  1. The upgrade must not be interrupted. do-release-upgrade is a long task: hundreds of megabytes to download, thousands of packages to unpack and configure. If the sshd binary gets replaced mid-stream and I lose the SSH connection, I have no console to fall back to.
  2. Services will flap. While v2ray’s package is being upgraded, systemd restarts it; for a few minutes every device in the house loses its tunnel. I can live with minutes; I cannot live with tens of minutes.
  3. I do not want to babysit dpkg. Watching Unpacking linux-headers-6.8.0-124-generic ... scroll by is a waste of attention.

Putting those three together, the answer is clear: let the Agent drive, and only review the result. I hand it one sentence; it strings together tmux-guarding, fallback sshd, do-release-upgrade, and log monitoring. If something fails, I can scroll back; if it succeeds, I drink tea.

This is the bird’s-eye view:

One-sentence upgrade: you on the couch, Agent in the other room dismantling the old cabinet and installing a new one

Figure 1: you (left) speak one sentence; the Agent goes through ssh + tmux to the remote box (right). The box upgrades from jammy 22.04 to noble 24.04. tmux keeps the process alive across disconnects; fallback sshd on :1022 covers the moment the main sshd gets replaced. You never leave the couch.


2. What the Agent does first: look at the box

By reflex, an Agent that receives a remote task starts with a probe. The probe is not “can I ping it”; it is “let me read its ID card”:

ssh root@host 'cat /etc/os-release; uname -a; uptime; df -h /; free -h; ss -tlnp | head -20'

What the Agent actually pays attention to in the output:

In everyday terms: the landlord looks at the floor plan first, to know whether it is a 90 m² two-bedroom or a 30 m² studio, before deciding which truck the moving company should send.

One detail matters here — Prompt=lts. Ubuntu’s release-upgrade defaults to the LTS track, but jammy → noble is a cross-LTS move; with a pure Prompt=lts the upgrader will not even notice noble exists. The Agent rewrites /etc/update-manager/release-upgrades to Prompt=normal so do-release-upgrade recognises noble as a target.


3. What the Agent does second: a tmux night-watch

Once the upgrade starts, the Agent cannot guarantee its own SSH link stays up the whole time. A network blip, the Agent’s own laptop sleeping, the Agent’s context window being truncated — any of those kills the SSH session.

If the original command was simply ssh root@host 'do-release-upgrade ...' &, when the parent SSH dies the kernel takes the remote upgrader down with it — the classic “killed because parent died”. A mid-run kill leaves apt locks held and the system half-upgraded; the next reboot is a coin flip.

It is like hiring a moving company and going downstairs for a coffee — if nobody answers the doorbell, the movers leave. You need someone standing at the gate the whole time, even while you are out.

tmux is that someone at the gate. The Agent opens a detached session on the remote side:

tmux new-session -d -s upg -c /root 'bash -lc "echo tmux-ready; bash"'

After that, every operation — including the actual do-release-upgrade — is delivered into the session via tmux send-keys. tmux runs as a server process on the remote box and does not depend on the SSH connection. If the Agent’s SSH drops ten times in a row, no big deal — tmux attach -t upg and you are right back where you were.

Why tmux and not nohup + &? Because tmux also gives you a replayable scrollback: you can scrub back and see which dpkg line ran, which package got stuck, what the journal said three minutes ago. That is a lifeline when something goes wrong.


4. What the Agent does third: a fallback sshd

During the upgrade, the openssh-server package itself gets replaced. The instant dpkg overwrites /usr/sbin/sshd, systemd kills the running sshd process and respawns the new binary.

The window is tiny, but it is not zero. If the Agent’s SSH happens to die inside that window:

Once the Agent cannot reconnect, it can never reconnect. The only options are driving to the data centre for a console (not possible) or waiting for the box to finish rebooting on its own (uncertain timing).

It is like replacing the elevator cables: while the main elevator is down, a backup elevator has to be standing by. You cannot leave the passengers stranded on the 20th floor.

The fix: before the upgrade starts, the Agent launches an independent sshd, outside of systemd, listening on port 1022:

ssh-keygen -A
/usr/sbin/sshd -D -p 1022 -o PidFile=/run/sshd-extra/sshd-1022.pid &

This sshd is not under systemd’s control, so dpkg swapping the package on disk cannot touch it — the running process already has the binary mapped into memory; rewriting the file does not affect a process that has already loaded it.

After this, every time the Agent’s SSH drops, it tries :22 first, then :1022. Port 1022 is the real escape hatch.

When the upgrade is done, the Agent kills that fallback process.


5. What the Agent does fourth: actually run the upgrade

With everything ready, the Agent types into the tmux session:

DEBIAN_FRONTEND=noninteractive do-release-upgrade -f DistUpgradeViewNonInteractive 2>&1 | tee /var/log/dist-upgrade.log

Two key switches:

After that, the Agent switches roles: from “driver” to “observer”. It stops sending commands and just polls — every 30 seconds it reads journalctl, captures the tmux pane, and greps for dpkg. Each tick is a heartbeat saying “still moving”.

The whole pipeline splits cleanly into five stages:

The Agent’s five-stage upgrade pipeline

Figure 2: the upgrade split into five stages. Every stage has a visible signal, and the Agent uses those signals to know whether it is safe to move to the next one.

What actually happens inside each stage — here is the EKG the Agent pulled out of journalctl:

Real timeline: 25 minutes from probe to new kernel online

Figure 3: an excerpt of the journalctl timeline for the same run. 10:14 — into dpkg. 10:21 — udev 255 (the noble version) comes online. 10:24 — autoremove releases the old packages. 10:28 — reboot. 10:35 — the new kernel 6.8.0-124 is up; sshd PID 805 owns port 22. The heartbeat is steady; nothing got stuck.

The official docs say “this can take one to several hours”. On this machine it was 27 minutes from the first SSH probe at 10:08 to the new kernel being online at 10:35. About 6 minutes were downloading packages on a 100-megabit link, about 14 minutes were the dpkg stage, and the rest was probing, waiting, and the reboot.


6. What the Agent does fifth: reboot and self-check

Once dpkg has finished, the upgrader asks once whether to reboot. DistUpgradeViewNonInteractive answers “yes”. systemd starts walking the shutdown dependency tree, and reboot.target finally fires.

What the Agent does from outside:

  1. Trigger the reboot from a nohup: nohup bash -c "sleep 2; /sbin/reboot" & — in the background so the Agent’s own SSH is not the one that gets killed.
  2. Wait for the host to drop.
  3. Wait for the host to come back.
  4. The instant it comes back, run:
ssh root@host 'cat /etc/os-release; uname -r; uptime; ss -tlnp; systemctl is-active ssh'

The expected output is:

PRETTY_NAME="Ubuntu 24.04.4 LTS (Noble Numbat)"
VERSION_ID="24.04"
VERSION_CODENAME=noble
6.8.0-124-generic
... up 0 min ...
LISTEN 0  4096  0.0.0.0:22   systemd
LISTEN 0  100  0.0.0.0:1080 python3
... (every original port still listening)
active

This step is how you prove the upgrade actually happened. “dpkg finished” alone is not enough — the kernel did not switch, systemd did not switch, that does not count. What counts is the version string you cat, and the kernel string uname prints.

Here is the Agent’s view of /etc/os-release (sanitised):

Verifying /etc/os-release

Figure 4: the post-upgrade “physical”. VERSION_CODENAME=noble is the ultimate verdict; VERSION_ID=24.04 is the secondary one; uname -r=6.8.0-124-generic is the kernel verdict. All three matching is what “really upgraded” means.

At this point the Agent reports back to me.


7. The two things you will ask first

7.1 Will the upgrade break my services?

Yes, but only a little.

During this run, both v2ray and my Python proxy services were restarted by systemd as their packages were upgraded. Because their units do not have any “wait for downstream dependency” condition, they came right back up and started listening again.

What can really go away:

7.2 How long will my network be down?

That depends on what kind of proxy you run. This box is SOCKS5 + HTTP, and clients usually have reconnect logic, so users feel a “stutter for tens of seconds”. But if you run long-lived TCP services (reverse SSH tunnels, WireGuard), they really do drop for that window.

Out of the 27 minutes, about 5 to 8 minutes are “service restart” time. The rest of the time the services are technically running, just slower — because dpkg is eating CPU.

7.3 What is left to do after the upgrade?

  1. Drop the old kernel: apt purge linux-image-5.15.0-101-generic — frees about 100M+.
  2. autoremove the stragglers: apt autoremove --purge — the upgrader usually leaves a few obsolete conffiles around.
  3. Diff your systemd drop-ins: compare sshd_config etc. against .dpkg-old to confirm your customisations are still there.
  4. Systemd-ify your naked scripts: see 7.1.
  5. Restore /etc/update-manager/release-upgrades to Prompt=lts. Otherwise the next careless do-release-upgrade will chase a non-LTS channel.

8. Reflections afterwards

For this upgrade, the Agent did everything end to end — probe, risk assessment, disconnects defence, run the upgrade, self-check, write a report. I personally did two things only: authorise once and approve once. The other 27 minutes I drank coffee.

This made me think again about where Agent capability actually ends:

An Agent is not thinking for you. It is executing for you the things you already know how to do, but do not want to stare at.

If you are using Claude Code, Cursor, Codex or any other Agent that can take an SSH handle, try a one-sentence upgrade once. The prerequisite is that you know the box well enough to take over if the Agent gets stuck.

Next time the box has to go 24.04 → 26.04, I will be even lazier — I will not even have to explain tmux and fallback sshd again.


References

  1. Ubuntu official upgrade docs: https://help.ubuntu.com/community/NobleUpgrades
  2. do-release-upgrade source and manpage: https://manpages.ubuntu.com/manpages/jammy/man8/do-release-upgrade.8.html
  3. Ubuntu Wiki — ReleaseUpgrade SOP: https://wiki.ubuntu.com/SystemMaintenance/ReleaseUpgrades
  4. tmux manual and best practices: https://github.com/tmux/tmux/wiki
  5. systemd service.d mechanism (drop-in override): https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html
  6. SSH fallback port on upgrade (launchpad bug 1192476 — the original justification for the 22→1022 fallback): https://bugs.launchpad.net/ubuntu-release-upgrader/+bug/1192476
  7. Ubuntu 24.04 LTS Noble Numbat desktop screenshots: Wikimedia Commons (CC BY-SA 4.0): https://commons.wikimedia.org/wiki/Category:Ubuntu_24.04

All IPs, hostnames, usernames, passwords, and configuration paths in this post have been sanitised before publication; actual network topology depends on your own environment.