UEFI 云服务器上折腾多系统引导,GRUB 那套配置我早就受够了。某次手滑改了 grub.cfg,结果连 rescue 模式都进不去。这种事不止一次,冷汗直接下来的时候,你才会想找个更靠谱的方案。

systemd-boot 是个清爽的选择。它直接读 /boot/loader/entries/ 下的 .conf 文件,每个条目对应一个内核或系统,结构简单到离谱。没有模块加载、没有复杂的脚本执行,就一个 boot loader 该干的活:找到 .efi 文件并启动。云服务器的 UEFI 固件和它配合得极好,冷启动时间削掉一大截。

注意:不是所有发行版默认都带了 systemd-boot。Ubuntu 从 22.04 后依然用 GRUB,Arch 可选但默认也不是,CentOS 9 的某些云镜像甚至直接跳过它。判断方法很简单——ls /boot/loader/entries/ 下有没有 .conf 文件,有就是 systemd-boot,否则大概率还是 GRUB。千万别光看发行版名字拍脑袋。

注意

迁移前务必备份现有 GRUB 配置,并确保有救援模式访问权限。搞砸引导不是闹着玩的,云控制台的 VNC 救急不一定每次都靠谱。

从GRUB迁走,没那么玄乎

别折腾 grub-mkconfig 了。你刚在云控制台里敲完 grub2-mkconfig -o /boot/grub2/grub.cfg,结果发现新内核没加进去——因为 /etc/default/grub 里漏了 GRUB_ENABLE_BLSCFG=true,而你的 CentOS Stream 9 镜像压根不认这行。这种事,systemd-boot 从根上就绕开了。

先确认你到底在用啥。别信发行版文档,直接跑:

ls /boot/loader/entries/ && echo "systemd-boot detected" || ls /boot/grub2/grub.cfg && echo "GRUB in use"

如果 /boot/loader/entries/ 是空目录或不存在,但 /boot/grub2/grub.cfg 存在且非零字节——恭喜,你正踩在 GRUB 的配置沼泽里。

装 bootloader 不是装软件包那么简单。 才是关键动作。它不依赖发行版的 meta 包名,而是直接把 /usr/lib/systemd/boot/efi/systemd-bootx64.efi 安装到 ESP 分区的 /EFI/systemd/ 下,并注册 EFI 启动项。CentOS 9、Anolis OS 8.8、甚至某些定制 Ubuntu 云镜像,只要内核 ≥5.10 + systemd ≥249,这条命令就能过。

写个 .conf 文件,比写 shell 脚本还直白。进 /boot/loader/entries/,新建

title CentOS Stream 9 (5.14.0-284.18.1.el9_2)
version 5.14.0-284.18.1.el9_2
linux /vmlinuz-5.14.0-284.18.1.el9_2
initrd /initramfs-5.14.0-284.18.1.el9_2.img
options root=UUID=xxxx ro rd.lvm.lv=centos/root rhgb quiet

改完记得 sync。没有模板引擎,没有变量展开,也没有 这种玄学命令——默认启动项就靠 loader.conf 里一行 default 定死。

迁移后第一次重启,你会明显感觉到那个“卡顿消失的瞬间”——不是快了 0.3 秒,是固件交出控制权后,systemd-boot 立刻跳转,中间没任何解析延迟。

systemd-boot configuration files

Linux 和 Windows 共存?手动来

云服务器上混装 Windows 和 Linux?别指望 systemd-boot 自动发现 Windows Boot Manager——它连 /boot/efi/EFI/Microsoft/Boot/bootmgfw.efi 都懒得扫一眼。

手动加条目,不是猜路径。进 /boot/loader/entries/,新建 win11.conf

title Windows 11 (Azure Gen2)
efi /EFI/Microsoft/Boot/bootmgfw.efi

注意:不是 /EFI/Windows/Boot/...,也不是 /boot/efi/EFI/... —— efi 行的路径是相对于 ESP 根目录的,而 ESP 通常挂载在 /boot/efi。实测 CentOS Stream 9 + Azure Gen2 VM 必须用这个路径,写错一个斜杠就黑屏回退到固件菜单。

Linux 发行版参数各玩各的。Anolis OS 8.8 要加 rd.md=0 rd.lvm=0 关掉 RAID/LVM 自动扫描,否则卡在 systemd-coredump[1]: Process 432 (systemd-logind) of user 0 dumped core.;CentOS Stream 9 同一内核版本却要保留 rd.lvm.lv=centos/root。别抄博客,cat /proc/cmdline 看当前跑着的参数才是唯一真相。

UUID 别手敲,blkid 是救命绳。执行:
sudo blkid -s UUID -o value /dev/nvme0n1p2
直接粘贴结果进 options root=UUID=...。有一次镜像重装后,有人把 nvme0n1p2 的 UUID 错贴成 nvme0n1p1(ESP 分区),systemd-boot 默默跳过该条目,启动直接 fallback 到 Windows —— 连错误日志都不打。

多系统不是功能开关,是路径、UUID、固件信任链三者咬死的活扣。松一环,整个引导就静音了。

Windows and Linux dual boot UEFI

内核回退:别等到黑屏才后悔

systemd-boot 菜单跳得再快,遇上内核崩溃一样黑屏。上个月一台 CentOS Stream 9 云主机在 yum update 后重启,直接卡在 systemd-coredump[1]: Process 432 (systemd-logind) of user 0 dumped core. —— 新内核打了某个安全补丁后,跟云厂商的定制 VirtIO 驱动冲突了。这时候你需要的不是重装,是冷静地选回旧内核。

保留旧内核不是怂,是底线。很多人迁移到 systemd-boot 后,只留了最新内核的 .conf 文件。这是给自己埋雷。/boot/loader/entries/ 里至少保留两个条目:最新的稳定版,以及上一个能正常启动的版本。我习惯直接复制一份 cp /boot/loader/entries/5.14.0-427.el9.x86_64.conf /boot/loader/entries/5.14.0-427.el9.x86_64-fallback.conf,然后把 fallback 文件的 title 改成 CentOS Stream 9 (5.14.0-427.el9.x86_64 - fallback)。systemd-boot 的默认超时时间是 5 秒,够你看到菜单时按方向键停住。

注意:云服务器的串行控制台(如 Azure 的 Serial Console、AWS 的 EC2 Serial Console)需要在 options 行加上 console=ttyS0,115200n8,否则你看不到菜单。这个坑我踩过两次:第一次以为 systemd-boot 挂了,其实是串口没输出。

内核崩溃后,方向键是唯一的救命稻草。重启时狂按 DownEnd 键,systemd-boot 菜单会出现。选中 fallback 条目,按 Enter。如果连菜单都没出来,说明 EFI stub 加载之前就炸了——这时候你需要在固件设置里把 Secure Boot 暂时关掉,或者用 efibootmgr -n 0001 在下次重启时强制指定某条目。

我遇到过一种情况:新内核的 vmlinuz 文件损坏,systemd-boot 直接跳过该条目,连报错都不给。解决方案是在 /boot/loader/loader.conf 里加上 ,让固件输出更多调试信息。

# /boot/loader/loader.conf
default centos-stream-9-5.14.0-427.el9.x86_64.conf
timeout 5
console-mode keep   # 保留固件控制台输出,方便看错误
editor 1            # 允许按 e 编辑内核参数,调试时极有用

旧内核一启动就 kernel panic,查了半天,其实是 initramfs 缺了磁盘控制器模块。云服务器的磁盘驱动比较特殊——Azure 的 ata_piix、AWS 的 xen-blkfront 这些,新内核会自动打包进去,但旧内核的 initramfs 里根本没它们。

真碰到了也别慌——急救模式能救你,或者拿发行版 ISO 挂载 chroot 进去,跑条命令就行。

# 先查看当前内核版本
ls /lib/modules/
# 假设要重建 5.14.0-427.el9.x86_64 的 initramfs
dracut -f --kver 5.14.0-427.el9.x86_64

-f 是强制覆盖,别漏了。如果你不确定缺什么驱动,用 --add-drivers "ata_piix xen-blkfront" 硬塞进去。更狠一点的做法:直接 dracut --force --hostonly --no-hostonly-cmdline,让它从当前运行的内核里自动推断需要的模块。不过 --hostonly 在云服务器上有时会漏掉某些热插拔存储设备,建议重建后 lsinitrd /boot/initramfs-5.14.0-427.el9.x86_64.img | grep -i virtio 确认驱动在不在。

最后一步:把 systemd-boot 的默认条目切回旧内核。编辑 /boot/loader/loader.confdefault 行,指向 fallback 的 conf 文件。下次重启前,记得先 sync 一下。这个细节让我少跑两趟机房:不 sync 可能导致刚改的 loader.conf 没落盘,重启后还是用损坏的新内核。

内核回退说到底就是个备胎方案,systemd-boot 只管帮你把备胎挂上去,真正决定系统能否正常启动的,还是 initramfs 和内核模块那根链条。别总迷信“最新”俩字儿——翻一次车你就懂了。

实战:阿里云 ECS 的 Anolis 23 翻车

上周三凌晨,一台刚从 CentOS 8 迁到 Anolis OS 23 的阿里云 ECS 实例,在 reboot 后卡在黑屏——连 systemd-boot 菜单都没弹出来,只有一行 Failed to load image \EFI\anlis\kernel-6.1.0-11.1.an23.x86_64.efi: Not Found

别急着重装,先摸清 boot 分区在哪。用同版本 Anolis ISO 启动急救模式,lsblk -f 看出 ESP 分区挂载在 /mnt/boot/efi,但 /mnt/boot/efi/EFI/anlis/ 下确实缺那个 kernel 文件——原来迁移脚本漏了 cp /boot/vmlinuz-6.1.0-11.1.an23.x86_64 /mnt/boot/efi/EFI/anlis/ 这步。

chroot 里补全再生成 loader.conf。挂载好 /mnt/{proc,sys,dev,run} 后 chroot 进去,执行:

cp /mnt/boot/vmlinuz-6.1.0-11.1.an23.x86_64 /mnt/boot/efi/EFI/anlis/
bootctl update --esp-path=/mnt/boot/efi

注意:Anolis 23 的 bootctl 默认不扫描 /boot/efi/EFI/anlis/,得先 echo "default anlis-kernel-5.14.0-427.conf" > /boot/loader/loader.conf 手动指定 fallback。

重启前,我盯着 efibootmgr -v 输出确认条目已更新——这次没忘 sync。

翻车不可怕,可怕的是不知道怎么救。systemd-boot 的简洁意味着你要对每个条目负责,但反过来,出了问题你也能五分钟内定位。说到底,引导这层东西越透明,你越睡得踏实。