boot-manager(8)
NAME
boot-manager - kernel and boot-entry custody for atomic, slice-based systems
SYNOPSIS
boot-manager update [options]
boot-manager mount-modules [options]
boot-manager report-booted [kver] [options]
boot-manager [--help | --version]
DESCRIPTION
On a conventional system, removing an old kernel means deleting its files. On an atomic system you can't do that. The kernels live in /usr, /usr is a sealed, signed image, and the next update replaces it wholesale -- taking the kernel you were going to fall back to right along with it.
boot-manager fixes that by taking custody. It owns which kernels a system can boot, and the boot menu that offers them.
What does it keep?
Per kernel type, it keeps:
- the running kernel,
- the last kernel that booted successfully,
- up to extra older kernels that booted successfully,
- a not-yet-booted update,
- the current version of each installed kernel package.
Every one of them comes with its matching /lib/modules/<kver>. That list is the keep-set. Everything else gets reaped.
How does it keep them, if /usr is read-only?
It copies them out. Each kept kernel is copied from /usr/lib/kernels into boot-manager's own durable cache under /var/lib/boot-manager. The keep-set is then mirrored onto the ESP as Boot Loader Specification Type 1 entries, and reaping happens in that cache. /usr is never written to.
Why mount every kernel?
A kernel in the menu is a promise that it will boot. So every kept kernel is made fully resident: its image is mounted read-only, and a writable overlay is stacked over its module tree. That leaves room for locally built (DKMS) modules without breaking the signed base.
Why is there no command line for any of this?
Because the kernel package's post-transaction trigger runs boot-manager update with no arguments. All configuration comes from boot-manager.toml(5), and nothing else.
What if it can't tell which kernel is running?
Then update refuses to reap, full stop. The keep-set is still ensured, nothing is removed, and the report says so. Reaping blind could remove the kernel you're running on.
What if the running kernel can't mount a new image?
With initrd tier none, a refused mount (an image the running kernel can't decompress, say) doesn't keep the new kernel out of the menu. update reads vmlinuz out of the image with fsck.erofs(1) instead, and warns that this kernel's modules come up when it boots. Later passes try the real mount again. With a tier that reads modules, there is no such fallback: an initrd built from an empty module tree would have no drivers in it.
Who owns the saved default?
update also keeps the loader's saved default current. That's firmware state, and a user can set it from the menu too (the d key). So boot-manager moves it only when it's the one who put it there.
It seeds an unset default, repairs one that names a reaped entry, and advances its own earlier seed as newer kernels prove themselves. A default a user picked is left alone for as long as its entry exists. One that names an entry that isn't boot-manager's (another operating system's, say) is never touched. See /var/lib/boot-manager/default-seeded under FILES.
What about a kernel that hasn't booted yet?
The saved default only ever names a kernel that has booted successfully. A tip that hasn't is set as the loader's one-shot entry instead. The next boot tries it once, and if it fails, the boot after that falls back to the saved default.
The one-shot is left unset for a tip whose boot counter is spent, and while a user's own pick is the default.
COMMANDS
update
The reconcile pass. It rolls the boot menu to the keep-set: it imports each kept kernel from /usr into the cache, materializes it, resolves its initrd, writes its boot entry, and then reaps everything outside the keep-set from both the ESP and the cache. Running it twice does no harm.
A kernel package calls it after installing or removing a kernel, by way of the boot-manager-update.service unit. The unit is where the ESP and cache mounts this pass writes to are named, which is something a package trigger has no way to express.
It prints what it kept and what it reaped, and where the saved default and the one-shot entry went.
mount-modules
Serves the running kernel's modules at /lib/modules/<kver>.
update materializes kernels with mounts, and mounts don't survive a reboot. Without this, the tree is empty on a fresh boot, and every modprobe(8), every udev MODALIAS hotplug and every in-kernel request_module fails. So it runs once per boot, early, from the boot-manager-modules.service unit. Running it again when the tree is already served does nothing.
report-booted
Marks kver as having booted successfully. Without kver, that's the running kernel.
The marker is what makes a kernel eligible as the last-known-good fallback, so it must only be recorded after the boot is assessed good. The boot-manager-report-booted.service unit runs it after boot-complete.target, which systemd reaches only once boot assessment has passed. See systemd.special(7).
When the marked kernel is the running one, the saved default is then reconsidered by the same rules update applies. A tip that just proved itself becomes the default now, not at the next kernel install. Nothing is copied, reaped or armed as a one-shot.
The marker is written first. If updating the default fails, the kernel is still marked good.
Marking some other kver by hand is a repair of the record, not a boot, so it writes the marker and leaves the default alone.
OPTIONS
Global
-v,--verbose- Trace what is being done: every kernel found in the cache and in /usr, the running kernel and the per-type tips, why each kernel is kept or reaped, and each copy, mount, extraction, initrd generation, ESP write and external command as it happens. The trace goes to standard error, so the report on standard output stays the same. Accepted anywhere on the command line, before or after the subcommand. There's one level. No
-vv. -h,--help- Print usage to standard error and exit. Running
boot-managerwith no arguments, or asboot-manager help, does the same. -V,--version- Print the version and exit.
boot-manager versionworks too.
update
--configpath- Configuration file. Defaults to /etc/boot-manager.toml. A missing default is not an error: every setting takes its default, and
updatesays so on standard error. A missing file you named here is an error. --statedir- State directory: the kernel cache, generated initrds and boot markers. Defaults to /var/lib/boot-manager.
--usrdir- Kernel source directory. Defaults to /usr/lib/kernels.
--offlineThe image-build pass, for composing a bootable image instead of updating a running system. There's no running kernel, so the keep-set is ensured and nothing is reaped. And the kernel is read out of its image with fsck.erofs(1) instead of being loop-mounted, which an unprivileged image composer running in a user namespace can't do.
Point
--usr,--stateand the configuration's boot_root into the tree being composed. Both entry_token and cmdline must be set in the configuration. Offline, there is no fallback to /etc/machine-id and /etc/kernel/cmdline: those belong to the build host, and falling back to them would bake its identity and root device into somebody else's image.With tier
none, only vmlinuz is read out of the image. With tierhost, the kernel's module tree is read out as well, for the generator to build from, and removed again when the pass ends. It stands in for the module mount a running system gets, and would otherwise sit uncompressed in the image next to the kernel image it came from. The generated initrd stays cached under--state, so the composed system's firstupdatereuses it instead of generating it again.--sysrootdir- The root filesystem the pass is for, substituted for
{sysroot}in the initrd template. Defaults to /. With--offline, a template that names{sysroot}requires it. Offline, / is the build host, and an initrd generated from the build host's own tooling and configuration is an initrd for the wrong system. The pass is refused before it starts.
mount-modules
--statedir- State directory. Defaults to /var/lib/boot-manager.
--modules-rootdir- Where the module tree is served. Defaults to /lib/modules.
report-booted
--configpath- Configuration file, same as for
update. Its entry_token names the saved default's entries. --statedir- State directory. Defaults to /var/lib/boot-manager.
--usrdir- Kernel source directory, read for the per-type tips. Defaults to /usr/lib/kernels.
EXIT STATUS
0- Success.
1- Failure, with the reason on standard error. An unknown subcommand or flag is a failure too. A pass that couldn't determine the running kernel, and therefore skipped reaping, is not a failure: it succeeds, and says what it skipped.
FILES
- /etc/boot-manager.toml
- Configuration; see boot-manager.toml(5).
- /usr/lib/kernels/
- Kernel source, installed by the kernel package: one <kver>.erofs image, its <kver>.meta sidecar, an optional <kver>.erofs.sig and <kver>.devel.erofs, and a default-<type> symlink naming each type's current tip. Read-only to
boot-manager. - /var/lib/boot-manager/kernels/<kver>/
- The copied-out image and its mount points. This is the durable cache that lets a kept kernel outlive the /usr it came from.
- /var/lib/boot-manager/kernels/<kver>/tree/lib/modules/<kver>
- A symlink to that kernel's module tree, handed to the initrd generator as
{modules_dir}. - /var/lib/boot-manager/initrds/<kver>/
- Host-generated initrds, keyed by a hash of their inputs.
- /var/lib/boot-manager/booted/<kver>
- Successful-boot markers, written by
boot-manager report-booted. - /var/lib/boot-manager/default-seeded
- The kver
boot-managerlast wrote as the loader's saved default. A user's menu pick lands in the same firmware variable, so this record is how the two are told apart. A saved default that still matches it follows the tip as the tip moves. One that differs is a deliberate pick, and is left alone. - $BOOT/loader/entries/<token>-<kver>.conf
- The boot entries written per kept kernel, with the kernel and initrd copies under $BOOT/<token>/<kver>/.
- /usr/lib/systemd/system/boot-manager-update.service
- Runs
updateon demand, started by whatever installed a kernel. Not enabled: it has no [Install] section, and nothing runs it at boot. - /usr/lib/systemd/system/boot-manager-modules.service
- Runs
mount-modulesearly in every boot. - /usr/lib/systemd/system/boot-manager-report-booted.service
- Runs
report-bootedonce the boot is assessed good.
EXAMPLES
A kernel package transaction just finished, and you want to see the menu reconciled step by step:
boot-manager update -v
Seed a freshly composed image so it boots itself. Everything points into the image tree, never at the host:
boot-manager update --offline \ --config image/etc/boot-manager.toml \ --usr image/usr/lib/kernels \ --state image/var/lib/boot-manager
Install onto a mounted target whose storage needs an initrd, with a template that names {sysroot}:
boot-manager update --offline \ --config /mnt/etc/boot-manager.toml \ --usr /mnt/usr/lib/kernels \ --state /mnt/var/lib/boot-manager \ --sysroot /mnt
SEE ALSO
boot-manager.toml(5), bootctl(1), systemd-boot(7), systemd.special(7), kernel-install(8), depmod(8), fsck.erofs(1)
The design and the kernel-package contract ship as docs/DESIGN.md and docs/KERNEL-PACKAGE-CONTRACT.md in the source tree.
AUTHOR
Auke Kok <sofar@foo-projects.org>