The Ur Project

Documenting the craft of stacking things so they remain stacked. For a very long time.

boot-manager.toml(5)

NAME

boot-manager.toml - configuration for boot-manager(8)

SYNOPSIS

/etc/boot-manager.toml

DESCRIPTION

This file configures boot-manager(8), and it's the whole configuration surface. The kernel package's post-transaction trigger runs boot-manager update with no arguments, so every knob a system needs is set here and nowhere else.

Do I need one?

Not necessarily. A system with no /etc/boot-manager.toml runs entirely on the defaults below. A file you name explicitly with boot-manager update --config is different: that one must exist.

Every setting has a default except entry_token and cmdline. Those two fall back to the kernel-install(8) locations at run time -- but not offline. See COMPOSE-TIME USE, below.

SYNTAX

It's a small subset of TOML, parsed without a TOML library:

  • key = value lines, one per line, at the top level or in [initrd].
  • Values are double-quoted strings, bare integers, or a single-line array of double-quoted strings. An array element can't contain a comma.
  • [initrd] is the only section. Everything before it is top-level.
  • A # starts a comment, on a line of its own or after a value. A # inside a quoted string is part of the string -- kernel command lines may contain one.
  • Blank lines are ignored.

An unknown key, an unknown section, a malformed value, an unquoted string or stray text after a value is a parse error, and update stops. That's deliberate. A typo in boot configuration should fail loudly, not quietly drop a setting.

SETTINGS

Top level

boot_root path
$BOOT: the mounted EFI system partition, or the ext4 /boot on an XBOOTLDR split. Boot entries are written under <boot_root>/loader/entries/, and each kept kernel's image and initrd under <boot_root>/<entry_token>/<kver>/. Must not be empty. Default: /boot.
entry_token string
Namespaces this machine's entries and its kernel directories on $BOOT. It's the kernel-install(8) entry token, usually the machine ID. When unset, update reads /etc/machine-id at run time. No default.
cmdline string
The kernel command line, written as the options line of every entry. When unset, update reads /etc/kernel/cmdline at run time, the way kernel-install(8) does: lines starting with # are dropped, and the rest is joined with single spaces. No default.
timeout integer
Menu timeout in seconds, written to <boot_root>/loader/loader.conf. Only the default and timeout lines of that file belong to boot-manager. Any other line an administrator adds is kept. Default: 5.
extra integer
How many additional previously booted kernels to keep per kernel type, on top of the mandatory set: the running kernel, the last known good one, each type's tip, and a not-yet-booted update. 0 keeps only the mandatory set. Default: 3.
tries integer
systemd-boot boot-counting attempts, stamped into the entry filename of a kernel that hasn't booted successfully yet, as <token>-<kver>+<tries>.conf. Default: 3. See "How does boot counting fit in?" below.

[initrd]

tier string
Which initrd strategy update drives: none, host or thin. Default: none. See "Which tier do I want?" below.
template array of strings
The generator command line for tier host, as an argv, not a shell string. Required, and rejected when empty, if the tier is host. Ignored otherwise. See "Writing a template" below.

Which tier do I want?

If your kernel can find and mount its root filesystem on its own, you don't need an initrd at all, and you want none. If the storage driver is a module, you need one, and you want host.

none
No initrd. Entries carry no initrd line, and firmware boots the kernel directly. This is the default.
host
Generate an initrd on this machine, by running template.
thin
Reserved. Not implemented yet, and selecting it is an error.

Writing a template

boot-manager knows nothing about any particular generator. dracut, mkinitcpio or a script of your own all drop in here. Choosing the flags, including whether to bundle early microcode, is the template's business.

These placeholders are substituted per kernel:

  • {kver}: the kernel version being generated for.
  • {modules_dir}: that kernel's materialized module tree, as boot-manager serves it, at a path ending in lib/modules/<kver>. That's the shape dracut's --kmoddir insists on.
  • {output}: where the generated initrd must be written. A template that leaves this out produces nothing, and that's reported as an error.
  • {sysroot}: the root filesystem the initrd is for. That's / on a running system, and the --sysroot of an update --offline pass. Hand it to a generator that takes one (dracut's --sysroot), so its modules, rules and configuration come from that system, not from whatever machine runs the pass.

Generated initrds are cached under /var/lib/boot-manager/initrds/<kver>/, keyed by (kver, template). Change the template, and the next pass regenerates and reaps what it superseded. The key is taken before substitution, so a compose-time pass and the composed system's own passes share it.

How does boot counting fit in?

systemd-boot spends one of the tries per attempt by renaming the entry file. A kernel that never reaches boot-complete.target within them is sorted to the end of the menu. The count also bounds how often update arms an unproven tip as the one-shot entry: once the counter is spent, it isn't armed again.

It doesn't demote a saved default, though. That's why boot-manager never saves an unproven kernel as the default in the first place.

Set tries = 0 to turn counting off and write the plain entry name. That's the right setting for a boot loader that doesn't implement counting, which today is every loader except systemd-boot. Success markers don't depend on it either way. They're written to <state>/booted/ by boot-manager-report-booted.service, which pulls in boot-complete.target on its own where no loader has.

COMPOSE-TIME USE

boot-manager update --offline composes a bootable image instead of updating the running system, and there entry_token and cmdline are required.

Why no fallback?

Because the build host's /etc/machine-id and /etc/kernel/cmdline aren't the image's. A silent fallback would bake the builder's identity and root device into somebody else's image. An image builder knows both values, so it has to say so.

Does tier host work offline?

Yes. The compose pass reads the kernel's module tree out of its image for the generator, and removes it when the pass ends. A template that names {sysroot} then needs update --sysroot, since / is the build host, not the image. boot-manager refuses the pass without it.

boot_root should point into the tree being composed too, just like --usr and --state.

EXAMPLES

An atomic system whose kernel needs no initrd. This is about the shortest useful file:

boot_root = "/boot"
timeout   = 3
extra     = 1

[initrd]
tier = "none"

A conventional system generating initrds with dracut, keeping more history:

boot_root = "/boot"
cmdline   = "root=LABEL=root ro quiet"
timeout   = 5
extra     = 3

[initrd]
tier     = "host"
template = ["dracut", "--kmoddir", "{modules_dir}", "{output}", "{kver}"]

template must be a single-line array. There's no line continuation.

An image being composed, where both identity values are spelled out:

boot_root   = "/boot"
entry_token = "0a1b2c3d4e5f60718293a4b5c6d7e8f9"
cmdline     = "root=PARTLABEL=root ro"
timeout     = 3
extra       = 0

[initrd]
tier = "none"

A system installed onto hardware whose storage driver is a module. It generates a generic initrd with dracut, both at install time and on every later update:

boot_root   = "/boot"
entry_token = "0a1b2c3d4e5f60718293a4b5c6d7e8f9"
timeout     = 3
extra       = 1

[initrd]
tier     = "host"
template = ["dracut", "--sysroot", "{sysroot}", "--kmoddir", "{modules_dir}", "--no-hostonly", "--force", "{output}", "{kver}"]

FILES

/etc/boot-manager.toml
This file.
/etc/machine-id
Fallback for entry_token.
/etc/kernel/cmdline
Fallback for cmdline.

SEE ALSO

boot-manager(8), machine-id(5), kernel-install(8), systemd-boot(7), dracut(8)

AUTHOR

Auke Kok <sofar@foo-projects.org>