这两天折腾 [[Proxmox VE]] 硬盘迁移的时候,顺手看了眼版本,发现手上这台还停在 8.4,而 PVE 9 从 2025 年 8 月发布到现在已经迭代到 9.2 了。想着既然机器都拆过一遍,索性把升级也一并规划了,很多年前我写过一篇 Proxmox VE 从 6.x 升级到 7.x 的文章,当时还遇到了不少的问题,但是家里这台 PVE 单机比较简单,希望升级过程不要出什么差错。

Proxmox VE 8 升级到 9 的检查与流程示意

这篇就把 8 升 9 的完整流程记下来。

PVE 9 更新了什么

PVE 8 基于 Debian 12 Bookworm,PVE 9 基于 Debian 13 Trixie。这是一次大版本的底座更换,不是小修小补,所以整个升级本质上是一次 Debian 的 dist-upgrade,PVE 的组件只是跟着走。

如果你今天才动手,apt dist-upgrade 之后会是 9.2 这个版本(2026 年 5 月发布),组件情况大致是:Debian 13.5、Linux 7.0 内核、QEMU 11.0、LXC 7.0、ZFS 2.4,Ceph 方面 Squid 19.2.3 和 Tentacle 20.2.1 并存,新装默认用 Tentacle。这一路从 9.0 攒下来不少东西,值得升的理由主要有几个:

厚置备 LVM 共享存储终于支持快照了。以前用 iSCSI/FC 接 SAN 的人只能靠备份来做回滚点,这是个长期的痛点。

SDN 部分扩得很凶。9.0 引入了 fabrics 概念支持 OpenFabric 和 OSPF,9.2 又加上了 WireGuard 和 BGP,还有路由映射、前缀列表这些精细的路由过滤能力。做多节点互联的话变化很大。

集群资源调度器在 9.2 加了动态负载均衡,会根据节点和虚拟机的实时资源利用率自动迁移 HA 管理的虚拟机。以前 CRS 只在启动和恢复时介入,现在是持续的。

还有一些小而实在的:Web 界面终于适配了移动端,出门用手机看一眼节点状态不再是折磨;9.1 支持从 OCI 镜像创建 LXC 容器,等于把 Docker 生态的镜像直接拿来做系统容器;自定义 CPU 型号的管理搬进了 Web 界面。

升级前必须满足的条件

这几条是硬性的,不满足就别开始。

当前必须已经是最新的 8.4。pveversion 输出的版本号要是 8.4.1 或更高。如果你还在 8.2、8.3,先在 Bookworm 源上把 8.x 升到最新再说。

如果安装了 Ceph 必须保证是 Squid 19.2。ceph --version 确认一下,还在 Quincy 或 Reef 的话得先把 Ceph 升上去,再动 PVE 的版本。跨两个大版本同时升级是自找麻烦。

必须有带外访问手段。IPMI、iKVM 或者能物理接触到机器。这条我要特别强调,官方文档明确写了不要通过 GUI 的虚拟控制台执行升级,因为升级过程中 pveproxy 会重启,控制台连接会断,而 apt 事务被打断在中途是最难恢复的状态。如果只能走 SSH,务必先开一个 tmux 或 screen 会话,把升级命令跑在里面,这样连接断了进程还活着。

根分区至少留 5G 空闲,建议 10G 以上。以及所有虚拟机都有可用的备份。

pve8to9 这个脚本值得认真读

PVE 提供了 pve8to9 这个检查工具,默认只读取信息不做任何修改,可以放心随时跑。加 --full 会执行更完整的一轮检查,官方建议在正式开始前至少跑一次完整版。

pve8to9 --full

它的输出分成几个区块:包版本、集群健康、Ceph 状态、存储配置、虚拟机检查、杂项检查,最后给一个汇总。每条结果的前缀标明了严重程度,PASS 是通过,INFO 是提示,SKIP 是不适用,WARN 是警告,FAIL 是必须修复。

正确的用法是循环:跑一次,修掉报出来的问题,再跑一次,直到 FAILURES 归零。

我这台机器报出来的三个问题

先说那个 FAIL:

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

这个坑的来源是 Debian 13 把 systemd-boot 拆成了三个包:systemd-boot-efi 提供 EFI 二进制文件,systemd-boot-tools 提供 bootctl 之类的工具,而 systemd-boot 这个元包带着安装钩子,它的 postinst 脚本会主动把 systemd-boot 装成系统引导器。

问题在于 PVE 的引导是由 proxmox-boot-tool 或者 GRUB 管理的。一台实际跑 GRUB 的机器上装着这个元包,升级时钩子一执行,要么把 EFI 启动顺序改到一个没有内核配置的 systemd-boot 上,要么脚本直接报错返回非零码,让整个 apt 事务在引导包这一步中断。两种结果都很难看。

动手删之前,我建议先确认这台机器到底用的哪个引导器,不要凭包列表猜。最可靠的判断依据是当前 EFI 启动项指向 ESP 上的哪个文件:

efibootmgr -v
proxmox-boot-tool status
ls -R /boot/efi/EFI

我这台的输出是这样:

Boot0000* proxmox HD(2,GPT,...)/File(\EFI\proxmox\shimx64.efi)

配合 ESP 里只有 grubx64.efi、shimx64.efi、grub.cfg,而 proxmox-boot-tool status 报 /etc/kernel/proxmox-boot-uuids does not exist,可以确定走的是 shim 到 GRUB 的链路,属于传统的 GRUB 加 fstab 挂载 ESP 的安装方式。这个 systemd-boot 包纯粹是历史遗留,删掉不影响启动。

PVE 的规律是:ZFS on root 的安装用 systemd-boot 并由 proxmox-boot-tool 管理,LVM 加 UEFI 的安装用 GRUB 加 shim。所以看到 LVM 布局基本就能预判是 GRUB。

删之前可以先模拟一次,确认不会牵连别的包:

apt-get -s remove systemd-boot

我这边的模拟结果只删一个包,因为 systemd 和 proxmox-kernel-helper 对它都只是 Suggests 级别的关系,不是硬依赖。确认之后执行:

apt remove systemd-boot

注意 systemd-boot-efi 要保留,别一起删了。反过来说,如果你确实是自己手动配置了 systemd-boot 引导,那就不要删,这时候脚本的建议不适用于你。

第二个是 WARN:

WARN: 1 running guest(s) detected

单节点没地方迁移,qm shutdown 关掉就是。为什么必须关:升级会替换 QEMU 二进制和内核,正在运行的虚拟机会处于「进程里跑的是旧版 QEMU,磁盘上已经是新版」的割裂状态,之后的热迁移、快照等操作都可能失败。

第三个是 NOTICE,不算错误:

NOTICE: storage 'local-lvm' has guest volumes with autoactivation enabled

PVE 9 开始,新建的 LVM 和 LVM-thin 虚拟机卷默认不再开启 autoactivation,改成由 PVE 按需激活。这个改动是为共享存储场景做的:旧行为下系统启动会激活 VG 里所有 LV,在多节点共用同一组 LUN 的环境里,两个节点同时激活同一个卷可能导致数据损坏,也会让虚拟机创建和迁移莫名其妙失败。

官方给了迁移脚本把已有卷转换过去:

/usr/share/pve-manager/migrations/pve-lvm-disable-autoactivation

共享存储强烈建议执行,纯本地存储可选。我这台全是本地盘,检查脚本自己也说了通常不会有问题,所以不着急。

开始升级

这是升级流程里操作最琐碎的一段。先确保 8.4 本身是最新的:

apt update && apt dist-upgrade
pveversion

这里确保输出的 PVE 版本是 8.4.x。然后最终跑一次 pve8to9 --full 确保解决了所有问题。如果检查见过中出现错误,必须先解决。

然后把 Debian 主源从 bookworm 改到 trixie:

sed -i 's/bookworm/trixie/g' /etc/apt/sources.list
sed -i 's/bookworm/trixie/g' /etc/apt/sources.list.d/*.list

PVE 自己的源建议直接改成 Trixie 时代的 deb822 格式。企业源写到 /etc/apt/sources.list.d/pve-enterprise.sources:

cat > /etc/apt/sources.list.d/pve-enterprise.sources << EOF
Types: deb
URIs: https://enterprise.proxmox.com/debian/pve
Suites: trixie
Components: pve-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF

没有订阅的话,写到 /etc/apt/sources.list.d/proxmox.sources,URI 换成 http://download.proxmox.com/debian/pve,Components 换成 pve-no-subscription。

用了 Ceph 的话,/etc/apt/sources.list.d/ceph.sources 同理,源地址指向 ceph-squid 目录。

改完之后用这两条命令验证:

apt update
apt policy

apt policy 能让你看清楚每个仓库的实际优先级和 suite,比单纯看 apt update 有没有报错靠谱得多。另外,所有 backports 相关的行都要注释掉或删掉,backports 里的包版本关系在跨大版本升级时经常打架。

如果 apt update 在 Ceph 源上报 401,说明订阅信息需要刷新:

pvesubscription update --force

执行升级与配置文件的取舍

正式开始:

apt dist-upgrade

耗时差异很大,SSD 上可能五分钟以内,机械盘上可能超过一小时。中途 dpkg 会就一批配置文件冲突征求意见,这里的选择是有讲究的,选错了不会立刻爆炸但会留下隐患。

保留你自己版本的:/etc/issue(登录提示,PVE 会自己重新生成)和 /etc/default/grub(你可能改过内核参数,比如 IOMMU 相关的设置,覆盖掉的话 [[PVE 直通 USB]] 之类的直通配置会失效)。

采用维护者版本的:/etc/lvm/lvm.conf(新版有必要的 thin_check 参数变化)、/etc/chrony/chrony.conf(时间同步配置结构变了)。

/etc/ssh/sshd_config 建议采用维护者版本,新版会把已废弃的 ChallengeResponseAuthentication 选项去掉。不过如果你自定义过 SSH 端口或者密钥认证策略,接受新版之后记得立刻检查一遍,别把自己关在门外——这种情况下保持一个已登录的 SSH 会话不要退出是保命的做法。

升级完成后再跑一次 pve8to9,然后必须重启。即使你升级前已经在跑 6.14 内核也要重启,因为 PVE 9 的内核是用新的编译器和 ABI 版本重新构建的,和用户空间的组件需要对齐。

那些真正会咬人的坑

前面几条属于流程,下面这些是环境相关的,命中哪条取决于你的机器。

网卡可能改名。这是我个人认为杀伤力最大的一条,尤其对没有带外管理的家用机器。跨大版本升级时 systemd 的可预测网卡命名规则可能变化,enp1s0 变成 enp2s0f0 这种事完全可能发生,而 /etc/network/interfaces 里的名字还是旧的,重启之后就是彻底失联。PVE 9 提供了 pve-network-interface-pinning 工具,可以把网卡固定成 nic0、nic1 这样的稳定名字,建议升级前就先做这个绑定。

说到这个,我上次就是因为换机器导致网口名字对不上而失联的,最后是靠安卓手机的 USB 网络共享救回来的,那次经历我单独写了一篇 把 Android 手机当成 USB 无线网卡,如果你也没有带外管理,可以提前把这招准备好。

PCI 直通可能失效。6.14 内核上有报告说部分设备的直通会失败,遇到的话可以先固定回旧内核再排查。做了显卡直通、网卡直通的机器,升级前建议先确认自己的设备型号有没有相关报告。

cgroup v1 彻底移除了。运行 systemd 230 及更早版本的容器不再受支持,具体来说就是 CentOS 7 和 Ubuntu 16.04 这类老系统的 LXC 容器,升级后起不来。手上还有这种容器的话,升级前就得先规划迁移。

/etc/sysctl.conf 不再被读取。Debian 13 改成只读 /etc/sysctl.d/ 下的文件,所以你在 sysctl.conf 里写的内核参数(比如 IP 转发、网络缓冲区调优)会静默失效。要挪到 /etc/sysctl.d/99-custom.conf 这样的文件里。

/tmp 变成 tmpfs 了,大小上限是内存的一半,而且 /tmp 和 /var/tmp 会被定期清理。如果你有脚本往 /tmp 放大文件或者放需要跨重启保留的东西,得改。

第三方存储插件必须先适配。用了社区的存储插件的话,升级前确认插件作者已经发布了 PVE 9 兼容版本,否则存储直接不可用。GlusterFS 存储插件在 9.0 被移除了,还在用的需要提前迁移。

其他零碎的:Veeam 备份在 QEMU machine 版本 10.0 及以上会出问题,需要把虚拟机的 machine 版本钉在 9.2+pve1;NVIDIA vGPU 需要 GRID 18.3 以上;maxfiles 这个备份保留选项不再支持;VM.Monitor 权限被移除,如果你的 API token 或自定义角色用到了它,权限模型要重新梳理;十年以上的老硬件测试覆盖有限,部分老 AMD 平台上 Ceph 出现过非法指令错误。

升级过程中如果被审计日志刷屏,可以临时关掉:

systemctl disable --now systemd-journald-audit.socket

升级后的收尾

重启后先强制刷新浏览器,Windows 下 Ctrl+Shift+R,macOS 下 Command+Option+R。Web 界面的 JS 和 CSS 变化很大,用了缓存的旧资源会出现各种奇怪的显示问题,这是新手最容易误判成「升级失败」的现象。

然后确认版本和服务状态:

pveversion -v
systemctl status pveproxy pvedaemon pvestatd pvescheduler

集群环境下每个节点重复一遍前面的完整流程。全部节点都到 9 之后,HA 组会自动转换成新的 HA 规则,转换过程可以用 journalctl -eu pve-ha-crm 观察。

最后可以选择性地把剩下的传统格式源统一转成 deb822:

apt modernize-sources

这个命令会先给出预览,问你是否执行时可以回答 n 先看看它打算改什么,实际执行时旧文件会保留成 .list.bak,不用担心改坏。

最后

提醒大家,pve8to9 这个命令行检查,在升级之前一定要检查。系统升级的风险从来不是均匀分布的,绝大部分风险集中在引导、网络、存储这三块,而这三块恰好都是失败之后很难再连上机器去修的地方。

顺着这个思路,我给自己定了几条原则:动 apt 之前先跑检查脚本并且清零;升级前先把网卡名字固定住;确保有一条不依赖 PVE 自身服务的登录通道,无论是 IPMI 还是一台插着 USB 的安卓手机。这些准备加起来不到半小时,但它们决定了万一出事的时候,你是坐在电脑前敲几条命令,还是要抱着显示器和键盘跑去机器跟前。

我这台机器接下来的计划是先删掉 systemd-boot 元包,把闲置的那块大容量机械盘挂上做备份存储,给虚拟机做一次完整备份,然后再动升级。