# pfi-pve: no-downtime prep for the PVE 8 → 9 upgrade (Prime 2026-10-04: "drive the ana pve upgrade to proxmox 9"). # scripts/elway infra-ops@10.250.250.31 --playbook playbooks/pfi-pve-pve9-prep.yaml # Plan + rationale: docs/runbooks/pfi-pve-pve9-upgrade-plan.md. Template: playbooks/esh-nas-pve-pve9-prep.yaml. # Nothing here reboots or touches a guest. The new kernel, microcode, bootloader and NIC pins all take effect at the # NEXT boot, which is the planned reboot test on PVE 8, BEFORE any repo switch to trixie. # - latest 8.4 packages (it is on 8.3.5 / kernel 6.8.12-8; pve8to9 only exists from 8.4); # - non-free-firmware + intel-microcode; # - grub-pc → grub-efi-amd64. The box boots UEFI from \EFI\proxmox\grubx64.efi, but the BIOS meta package is the # one installed, so that EFI binary has not been touched since the 2023-03-30 (PVE 7.4 / bullseye) install. The # PVE 8→9 wiki's "UEFI systems with root on LVM may fail to boot" fix is exactly this package. The 2023 binary is # copied to EFI/proxmox-2023/ as a last resort only: once grub-install replaces /boot/grub/x86_64-efi, an old core # loading new modules can fail (the grub_is_lockdown class), so the real fallback is the tar of /boot/grub; # - pin every NIC name by MAC. vmbr0 has four ports and only eno8303 has carrier; the two bnxt ports carry an # `np0/np1` phys_port_name suffix, the class of name the wiki says a new kernel can change. vars: stage_dir: /root/pve9-prep-20261004 steps: - name: back up /etc, the package list and the 2023 EFI binaries sudo: true shell: | set -e mkdir -p {{ stage_dir }} tar czf {{ stage_dir }}/etc-before.tar.gz -C / etc 2>/dev/null dpkg -l > {{ stage_dir }}/dpkg-before.txt pveversion -v > {{ stage_dir }}/pveversion-before.txt 2>&1 cp -p /etc/network/interfaces {{ stage_dir }}/interfaces.before cp -p /etc/apt/sources.list {{ stage_dir }}/sources.list.before cp -a /boot/efi/EFI {{ stage_dir }}/EFI-2023 cp -a /boot/grub {{ stage_dir }}/boot-grub-before ls -la {{ stage_dir }} creates: "{{ stage_dir }}/etc-before.tar.gz" - name: keep the 2023 GRUB bootable from the ESP (EFI/proxmox-2023/) sudo: true shell: | set -e mkdir -p /boot/efi/EFI/proxmox-2023 cp -p /boot/efi/EFI/proxmox/grubx64.efi /boot/efi/EFI/proxmox-2023/grubx64.efi md5sum /boot/efi/EFI/proxmox-2023/grubx64.efi creates: /boot/efi/EFI/proxmox-2023/grubx64.efi - name: add non-free-firmware to the Debian bookworm lines (for intel-microcode) sudo: true shell: sed -i -E '/^deb .*debian(\.org)?(\/debian)? bookworm/{/non-free-firmware/!s/$/ non-free-firmware/}' /etc/apt/sources.list when: "grep -E '^deb .*debian(\\.org)?(/debian)? bookworm' /etc/apt/sources.list | grep -qv non-free-firmware" - name: apt update (must be clean) sudo: true shell: | ! apt-get update -q 2>&1 | grep -E '^(E|Err):' changed_when: "false" - name: latest 8.4 packages (keep local configs) sudo: true shell: | set -o pipefail DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y -q \ -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold 2>&1 | tail -8 pveversion | cat - name: intel-microcode sudo: true shell: DEBIAN_FRONTEND=noninteractive apt-get install -y -q intel-microcode 2>&1 | tail -3 when: "! dpkg-query -W -f='${Status}' intel-microcode 2>/dev/null | grep -q 'install ok installed'" - name: grub-pc → grub-efi-amd64, with the removable-path copy (EFI/BOOT/BOOTX64.EFI) kept in step sudo: true shell: | set -o pipefail echo 'grub-efi-amd64 grub2/force_efi_extra_removable boolean true' | debconf-set-selections -v -u DEBIAN_FRONTEND=noninteractive apt-get install -y -q --no-install-recommends grub-efi-amd64 2>&1 | tail -8 when: "! dpkg-query -W -f='${Status}' grub-efi-amd64 2>/dev/null | grep -q 'install ok installed'" - name: make sure the ESP binary was actually rewritten (grub-install explicitly if the postinst skipped it) sudo: true shell: | set -e grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=proxmox --force-extra-removable 2>&1 | tail -3 update-grub 2>&1 | tail -3 when: "test \"$(md5sum < /boot/efi/EFI/proxmox/grubx64.efi | cut -d' ' -f1)\" = a4a8d8d1e75eb2dad19c22a8c1065c4c" - name: pin every NIC name to its MAC (systemd .link; takes effect next boot) sudo: true shell: | set -e pin() { # name mac cat > /etc/systemd/network/10-pin-$1.link <&1 | tail -1; done when: "! lsinitramfs /boot/initrd.img-$(ls /boot/vmlinuz-* | sed 's#/boot/vmlinuz-##' | sort -V | tail -1) | grep -q 10-pin-eno8303.link" verify: - name: interfaces file still parses (no live reload) sudo: true shell: ifreload -a -s changed_when: "false" - name: newest kernel's initramfs carries the uplink pin sudo: true shell: lsinitramfs /boot/initrd.img-$(ls /boot/vmlinuz-* | sed 's#/boot/vmlinuz-##' | sort -V | tail -1) | grep -q 10-pin-eno8303.link changed_when: "false" - name: grub-efi-amd64 in, grub-pc out, intel-microcode in sudo: true shell: | dpkg-query -W -f='${Status}' grub-efi-amd64 | grep -q 'install ok installed' && ! dpkg-query -W -f='${Status}' grub-pc 2>/dev/null | grep -q 'install ok installed' && dpkg-query -W -f='${Status}' intel-microcode | grep -q 'install ok installed' changed_when: "false" - name: ESP carries a NEW grubx64.efi, the removable copy matches it, the 2023 copy is kept sudo: true shell: | set -e a=$(md5sum < /boot/efi/EFI/proxmox/grubx64.efi | cut -d' ' -f1) b=$(md5sum < /boot/efi/EFI/BOOT/BOOTX64.EFI | cut -d' ' -f1) test "$a" != a4a8d8d1e75eb2dad19c22a8c1065c4c test "$a" = "$b" test "$(md5sum < /boot/efi/EFI/proxmox-2023/grubx64.efi | cut -d' ' -f1)" = a4a8d8d1e75eb2dad19c22a8c1065c4c ls -la --time-style=long-iso /boot/efi/EFI/proxmox/ /boot/efi/EFI/BOOT/ changed_when: "false" - name: firmware boots the proxmox entry first sudo: true shell: | efibootmgr -v | grep -E '^BootOrder|proxmox' first=$(efibootmgr | sed -n 's/^BootOrder: \([0-9A-F]*\).*/\1/p') efibootmgr -v | grep -E "^Boot${first}\*? " | grep -qi 'EFI\\proxmox\\grubx64.efi' changed_when: "false" - name: pve8to9 --full summary sudo: true shell: pve8to9 --full 2>&1 | grep -E '^(FAIL|WARN):|^= SUMMARY|TOTAL|PASSED|SKIPPED|WARNINGS|FAILURES' | cat changed_when: "false"