Upgrading my Five-Node Proxmox VE homelab from 8 to 9: Failures, Recovery, and Lessons Learned

A real Proxmox VE 8-to-9 upgrade across five lab servers: systemd-boot, GRUB, duplicate repositories, Ceph conflicts, and recovery.

This post is also available in: Spanish

Upgrading one Proxmox VE server is straightforward when repositories, boot packages, and storage dependencies are already consistent. Upgrading five servers in a small datacenter exposes every inconsistency that accumulated over previous installations and updates.

My lab contains five Proxmox VE hosts. I upgraded them one at a time to reduce the blast radius. Before working on each node, I stopped its virtual machines and LXC containers, updated the existing Proxmox VE 8 installation, changed the Debian and Proxmox repositories, performed the distribution upgrade, and validated the node before moving to the next server.

+---------------------------+
| Administrator |
+-------------+-------------+
|
Node-by-node migration
Proxmox VE 8 → PVE 9.2.6
|
v
+-------------------------------------------+
| Process for each node |
| |
| 1. Stop virtual machines and containers |
| 2. Fully update Proxmox VE 8 |
| 3. Run pve8to9 --full |
| 4. Change repositories to Trixie |
| 5. Simulate apt full-upgrade |
| 6. Upgrade and reboot the node |
| 7. Validate services and workloads |
+---------------------+---------------------+
|
v
+--------------------------------+
| CDMX - Primary |
| 172.16.10.100 |
| PVE 8 → PVE 9.2.6 |
+---------------+----------------+
|
+----------------------+----------------------+
| | |
v v v
+-----------------------+ +-----------------------+ +-----------------------+
| Mazatlán | | Mérida | | Guadalajara |
| 172.16.10.101 | | 172.16.10.102 | | 172.16.10.103 |
| PVE 8 → PVE 9.2.6 | | PVE 8 → PVE 9.2.6 | | PVE 8 → PVE 9.2.6 |
+-----------+-----------+ +-----------+-----------+ +-----------+-----------+
| | |
+-------------------------+-------------------------+
|
v
+-----------------------+
| Puebla |
| 172.16.10.104 |
| PVE 8 → PVE 9.2.6 |
+-----------+-----------+
|
v
+-------------------------------------------+
| Mini datacenter upgraded |
| 5 nodes running Proxmox VE 9.2.6 |
+-------------------------------------------+

That was the plan. In practice, I encountered four important problems:

  1. A systemd-boot meta-package that the pve8to9 checker required me to remove.

  2. A removable GRUB loader that was not configured to receive updates.

  3. Duplicate and mixed Bookworm/Trixie repositories.

  4. Ceph packages from an old Bookworm backport that caused APT to propose removing proxmox-ve.

On two nodes, the Proxmox management stack was ultimately absent after the package transition. The virtual machines were not necessarily terminated immediately, but commands such as pveversion and services such as pveproxy, pvedaemon, and pvestatd were no longer available. That produced the web-interface message:

Connection error 595: Connection refused

This article documents the incident and, more importantly, the safeguards I added before upgrading the remaining nodes.

After repairing the affected hosts and applying those safeguards, I completed the upgrade across all five servers. The difficult nodes became canaries that improved the procedure for every node that followed.

This is a lab incident report, not a substitute for the official upgrade guide. Package versions change over time. Always use the current Proxmox VE 8 to 9 upgrade guide and evaluate every APT transaction before accepting it.

My upgrade strategy

I treated the five-server environment as a rolling maintenance operation:

  1. Confirm access through SSH and an out-of-band or local console.

  2. Back up critical guests and configuration.

  3. Stop the VMs and LXC containers on the node being upgraded.

  4. Fully update Proxmox VE 8 before changing repository suites.

  5. Run pve8to9 --full and resolve every failure.

  6. Change Debian and Proxmox repositories from Bookworm to Trixie.

  7. Run a simulated full upgrade and inspect removals.

  8. Run the real full upgrade only when the Proxmox stack remains installed.

  9. Validate packages, services, storage, networking, boot files, and guests.

  10. Reboot and observe the node before proceeding to the next server.

Stopping the guests was conservative, but it simplified recovery and eliminated guest workload changes while the host package set was transitioning.

Pre-upgrade baseline

Before changing repositories, I now collect a baseline on every host:

hostname
pveversion -v
cat /etc/os-release
uname -r
pvesm status
qm list
pct list
df -h / /boot /boot/efi 2>/dev/null
pve8to9 --full

I also save the package and repository state:

dpkg-query -W -f='${binary:Package}\t${Version}\n' > /root/packages-before-pve9.txt
grep -RnsE \
'^[[:space:]]*deb|^[[:space:]]*(Types|URIs|Suites|Components|Enabled):' \
/etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

The pve8to9 script should be run repeatedly. Its recommendations can change after a conflicting package is removed, a new package is installed, or the upgrade reaches another stage.

Issue 1: the systemd-boot meta-package warning

The first failure was:

FAIL: systemd-boot meta-package installed. This will cause problems on upgrades
of other boot-related packages. Remove 'systemd-boot'.

In Debian Trixie, systemd-boot was split into several packages. The systemd-boot meta-package contains hooks that can automatically manage the bootloader. Proxmox normally manages applicable EFI System Partitions through proxmox-boot-tool, so the meta-package can interfere with that workflow.

However, it must not be removed blindly. First, I identified the actual boot path:

test -d /sys/firmware/efi \
&& echo 'Boot mode: UEFI' \
|| echo 'Boot mode: BIOS'
bootctl status
efibootmgr -v
findmnt /boot /boot/efi
dpkg -l | grep -E 'proxmox-boot-tool|systemd-boot|grub'

On my cdmx node, the important findings were:

Boot mode: UEFI
systemd-boot not installed in ESP.
BootCurrent: 0000
File: \EFI\proxmox\shimx64.efi

The node was booting through the Proxmox signed shim and GRUB, not through systemd-boot. /etc/kernel/proxmox-boot-uuids did not exist because this GRUB installation was not managed as a proxmox-boot-tool ESP set. I therefore simulated removal first:

apt -s remove systemd-boot

APT proposed removing only systemd-boot, so I proceeded:

apt remove systemd-boot

I deliberately kept systemd-boot-efi and did not run apt autoremove. The old kernels remained useful as rollback options.

The exception is a host on which systemd-boot was manually installed and is genuinely used as the bootloader. That configuration must be assessed separately. The official upgrade checker is the authority for whether the meta-package should be removed.

Issue 2: GRUB did not update the removable EFI loader

During initramfs generation, one node reported:

Removable bootloader found at '/boot/efi/EFI/BOOT/BOOTX64.efi',
but GRUB packages not set up to update it!

The hook supplied the required configuration:

echo 'grub-efi-amd64 grub2/force_efi_extra_removable boolean true' \
| debconf-set-selections -v -u

It then instructed me to reinstall GRUB:

apt install --reinstall grub-efi-amd64

My first reinstall attempt failed because the host contained GRUB packages from two releases:

grub-efi-amd64 depends on grub-efi-amd64-bin (= 2.12-9+pmx2)
but 2.06-13+pmx7 is to be installed

The correct lesson was not to force one GRUB package. All GRUB components must come from the same repository generation. I paused the bootloader repair, corrected the repositories, completed the package transition, and only then reinstalled GRUB.

After the package set was consistent, I used:

apt install --reinstall grub-efi-amd64
update-grub
efibootmgr -v

I did not reboot while GRUB packages were mismatched. A stale fallback loader under EFI/BOOT/BOOTX64.EFI can load modules from a different GRUB version and leave the machine at a GRUB rescue prompt.

Issue 3: duplicate and mixed repositories

Several nodes had both traditional .list files and newer Deb822 .sources files. APT warned:

Target Packages (pve-no-subscription/binary-amd64/Packages)
is configured multiple times

On one node, the Proxmox VE repository still used Bookworm while another repository already used Trixie. At another point, no Debian base repository was active, so APT could see new Proxmox packages but could not obtain compatible Trixie versions of systemd, LVM, AppArmor, and other base dependencies.

I reviewed all active definitions rather than performing a global search-and-replace:

grep -RnsE \
'^[[:space:]]*deb|^[[:space:]]*(Types|URIs|Suites|Components|Enabled):' \
/etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

For PVE 9, the repository set must be internally consistent:

  • Debian trixie

  • Debian trixie-updates

  • Debian trixie-security

  • Proxmox VE trixie

  • The correct Ceph repository, if Ceph packages are installed

  • No active bookworm, bpo12, or duplicate definitions

I kept one definition for each repository and renamed obsolete files with a .disabled suffix so the change remained reversible.

Example Debian Deb822 configuration:

Types: deb
URIs: http://deb.debian.org/debian
Suites: trixie trixie-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security
Suites: trixie-security
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg

Example no-subscription PVE Deb822 configuration:

Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg

Repository definitions should be taken from the current Proxmox package repository documentation, especially when using an enterprise subscription or Ceph.

Issue 4: APT tried to remove proxmox-ve

The most serious warning appeared during the full upgrade:

W: (pve-apt-hook) You are attempting to remove the meta-package 'proxmox-ve'!

The hook correctly stopped the transaction. The wrong response would have been to create /please-remove-proxmox-ve and continue. That file is an explicit authorization to remove the Proxmox meta-package; it is not an upgrade fix.

APT was proposing a large Debian transition, including old libraries being replaced by t64 variants. Those replacements are expected in Trixie. Removing proxmox-ve, pve-manager, pve-qemu-kvm, qemu-server, or libpve-storage-perl is not expected.

I diagnosed the dependency chain with:

apt -s install proxmox-ve
apt-cache policy \
proxmox-ve pve-manager pve-qemu-kvm \
ceph-common librados2 librbd1 libcephfs2

The real conflict was Ceph:

ceph-common requires librbd1 (= 19.2.3-pve1)
but 19.2.5-1~bpo12+2 is selected

The affected nodes contained Ceph Squid client packages built as Debian 12 backports (~bpo12+2), while the configured Proxmox PVE 9 Ceph repository offered a mutually consistent -pve1 package family. APT preferred the numerically newer backport, but its exact-version dependencies were incompatible with the rest of the Trixie/PVE package set.

The resulting dependency chain was:

incompatible Ceph packages
-> libpve-storage-perl cannot be installed
-> pve-manager cannot be installed
-> proxmox-ve is selected for removal

Ceph safety boundary

Before changing Ceph packages, I checked whether a node actually provided Ceph services:

ceph -s
ceph version
dpkg -l | grep -E '^ii[[:space:]]+ceph-(mon|osd|mgr)'

My affected hosts contained Ceph client libraries but no Ceph MON, OSD, or MGR packages. That allowed me to align the client packages to the versions offered by the configured Proxmox Ceph Squid repository.

This distinction is critical. If a node is part of an active Ceph cluster, follow Proxmox's coordinated Ceph upgrade procedure. The PVE 8-to-9 documentation requires eligible Ceph deployments to reach Squid before the host upgrade. Do not copy a client-library downgrade command onto a Ceph storage node.

Incident-specific client package alignment

For my client-only nodes, the complete Ceph family had to be selected in one APT transaction because many packages use exact-version dependencies:

apt -s install --allow-downgrades \
ceph-common=19.2.3-pve1 \
ceph-fuse=19.2.3-pve1 \
libcephfs2=19.2.3-pve1 \
librados2=19.2.3-pve1 \
libradosstriper1=19.2.3-pve1 \
librbd1=19.2.3-pve1 \
librgw2=19.2.3-pve1 \
python3-ceph-common=19.2.3-pve1 \
python3-ceph-argparse=19.2.3-pve1 \
python3-cephfs=19.2.3-pve1 \
python3-rados=19.2.3-pve1 \
python3-rbd=19.2.3-pve1 \
python3-rgw=19.2.3-pve1 \
liblttng-ust1t64 \
proxmox-ve

The version above records my incident and will become outdated. Before using an equivalent command, inspect the current candidates:

apt-cache policy \
ceph-common ceph-fuse libcephfs2 \
librados2 libradosstriper1 librbd1 librgw2 \
python3-ceph-common python3-ceph-argparse \
python3-cephfs python3-rados python3-rbd python3-rgw

I only removed -s after verifying that the simulation installed or preserved the complete Proxmox stack and did not propose additional destructive removals.

Recovery when the Proxmox management stack is missing

On affected nodes, this command failed:

pveversion: command not found

The service units were also absent:

Unit pvedaemon.service not found.
Unit pvestatd.service not found.
Unit pveproxy.service not found.

That confirmed that this was not merely a stopped proxy. pve-manager and related packages had to be restored.

Before recovery, I avoided rebooting and checked whether guest processes were still running:

ps -eo pid,cmd | grep -E '[k]vm|[l]xc-start'

After aligning the Ceph clients and reinstalling proxmox-ve, I completed package configuration and restarted the management plane:

dpkg --configure -a
apt-get check
systemctl daemon-reload
systemctl enable --now pve-cluster
systemctl restart pvedaemon pvestatd pveproxy

I tested the local API directly:

curl -k -sS -o /dev/null -w 'HTTP %{http_code}\n' \
https://127.0.0.1:8006/api2/json/version

An HTTP 200 or 401 response proves that the API endpoint is responding. I then verified the package stack with pveversion -v and tested the web interface on port 8006.

A safer procedure for the remaining nodes

The incident changed my workflow. I added explicit gates rather than assuming that a successful apt update meant the repositories were correct.

1. Finish PVE 8 updates first

While repositories still point to Bookworm/PVE 8:

apt update
apt full-upgrade
pve8to9 --full

Resolve all failures before changing suites.

2. Inspect Ceph before changing repositories

ceph version 2>/dev/null
dpkg -l | grep -E '^ii[[:space:]]+ceph-(mon|osd|mgr)'
dpkg-query -W -f='${binary:Package}\t${Version}\n' 2>/dev/null \
| grep -E 'ceph|librados|librbd|librgw'

An actual Ceph cluster requires its own supported upgrade sequence. Client-only packages still need a repository that supplies a consistent package family.

3. Change repositories and verify every suite

After editing repository definitions:

apt update
grep -RnsE \
'^[[:space:]]*deb|^[[:space:]]*(Types|URIs|Suites|Components|Enabled):' \
/etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

No active Bookworm or duplicate entries should remain.

4. Require APT to preserve Proxmox

apt -s install proxmox-ve
apt -s full-upgrade

Read the complete REMOVED section. Stop immediately if it contains any of these:

proxmox-ve
pve-manager
pve-qemu-kvm
qemu-server
pve-container
pve-ha-manager
libpve-storage-perl

Do not treat the Proxmox APT hook as an obstacle to bypass. It is the final guardrail preventing a repository or dependency mistake from dismantling the management stack.

5. Use a full upgrade, not a plain upgrade

A major Debian release transition needs to install replacement dependencies and remove obsolete libraries. A plain apt upgrade kept hundreds of packages back in my environment. Once the simulation was clean, I used:

apt full-upgrade

6. Validate before reboot

dpkg --audit
apt-get check
pveversion -v
systemctl --no-pager --full status \
pve-cluster pvedaemon pvestatd pveproxy
pvesm status
qm list
pct list
update-grub
efibootmgr -v
ls -1 /boot/vmlinuz-*-pve

I retained at least one known-good PVE 8 kernel until the node completed its first successful PVE 9 boot. I did not run apt autoremove during the upgrade window.

What I learned

APT's removal plan matters more than the upgrade count

Messages such as “569 upgraded, 131 newly installed, 64 to remove” are not automatically wrong during a major Debian transition. The identity of the removed packages is what matters. Old libraries may legitimately be replaced; the Proxmox management stack may not.

Repository consistency includes storage clients

I initially focused on Debian and PVE repositories. The decisive conflict came from Ceph client packages with exact dependencies. Storage-related repositories deserve the same preflight scrutiny as the base operating system.

A meta-package is operationally important

proxmox-ve contains little software itself, but it anchors the supported package set. Its proposed removal signals that APT can no longer satisfy that set. The correct action is to repair the dependency graph, not authorize removal.

Bootloader repair must wait for package consistency

Reinstalling GRUB while grub-common, grub2-common, and grub-efi-amd64-bin came from different releases would have created a boot risk. Repository correction and package alignment came first.

One node at a time limited the damage

Because I upgraded serially, each failure became a new preflight test for the next server. The five-node project reinforced a simple infrastructure principle: canary the procedure, validate every gate, and only then repeat it.

Final checklist

  • Current backups exist and restore paths are known.

  • SSH and console access work.

  • Guests on the target node are stopped or migrated.

  • PVE 8 is fully updated before repository changes.

  • pve8to9 --full reports no unresolved failures.

  • The active bootloader is identified.

  • Ceph server roles and client package versions are known.

  • All repositories use the intended suite and none are duplicated.

  • apt -s install proxmox-ve succeeds.

  • apt -s full-upgrade preserves the Proxmox stack.

  • No /please-remove-proxmox-ve bypass file exists.

  • Package, service, storage, network, and boot checks pass before reboot.

  • The node is validated after reboot before upgrading the next server.

Conclusion

My Proxmox VE 8-to-9 upgrade was not a single command; it was a dependency, repository, storage-client, and bootloader migration across five different installation histories.

The systemd-boot warning was safe to resolve only after identifying the active GRUB path. The GRUB fallback loader could only be updated safely after all GRUB packages came from the same release. Duplicate and mixed repositories explained the packages kept back. Finally, incompatible Ceph client builds explained why APT wanted to remove proxmox-ve.

The most valuable command in the entire project was not apt full-upgrade. It was the simulation immediately before it:

apt -s full-upgrade

When a major infrastructure upgrade proposes removing its own management platform, stop. The warning is not the problem—it is evidence of the problem.

Comments