BOOTC-INSTALLATION(7)
π¦ NAME π¦
bootc-installation β installing "bootc compatible" images, bestie. This is BOOTC-INSTALLATION(7) from the Miscellaneous Information Manual, and we're about to make your whole OS situation make sense π
β¨ DESCRIPTION β¨
Okay so the big dream of the bootc project? Thinking about bootable operating systems as container images. Like, yes, actually. Docker/OCI container images are literally just tarballs wrapped with some JSON β that's it, that's the whole thing π«Ά
But to actually boot a system (bare metal or virtualized, doesn't matter), you need a few key components:
- bootloader
- kernel (and optionally initramfs)
- root filesystem (xfs/ext4/btrfs etc.)
Bootloader installation depends on the platform and the selected bootloader. For example, GRUB installation uses bootupd (https://github.com/coreos/bootupd/), while systemd-boot uses bootctl, and s390x uses zipl. Bootloader installation can also be disabled entirely. The default expectation is that bootloader contents and install logic come from the container image in a bootc based system. π
The Linux kernel (and optionally initramfs) is embedded in the container image; the canonical location is /usr/lib/modules/$kver/vmlinuz, and the initramfs should be in initramfs.img in that directory. Noted, memorized, iconic π
The bootc install command bridges the two worlds of a standard, runnable OCI image and a bootable system by running tooling logic embedded in the container image to create the filesystem and bootloader setup dynamically. This requires running the container via --privileged; it uses the running Linux kernel on the host to write the file content from the running container image β not the kernel inside the container. Read that twice, it matters fr fr.
There are two sub-commands: bootc install to-disk and bootc install to-filesystem.
However β and this is the slay part β nothing else (external) is required to perform a basic installation to disk. The container image itself comes with a baseline self-sufficient installer that sets things up ready to boot. It's giving self-made main character energy β¨
πΈ Internal vs external installers πΈ
The bootc install to-disk process only sets up a very simple filesystem layout, using the default filesystem type defined in the container image, plus hardcoded requisite platform-specific partitions such as the ESP.
In general, the to-disk flow should be considered mainly a "demo" for the bootc install to-filesystem flow, which can be used by "external" installers today. For example, in the Fedora/CentOS bootc project (https://docs.fedoraproject.org/en-US/bootc/), there are two "external" installers in Anaconda and bootc-image-builder.
More on this below. Stay with me bestie π
π Executing bootc install π
The two installation commands allow you to install the container image either directly to a block device (bootc install to-disk) or to an existing filesystem (bootc install to-filesystem).
The installation commands MUST be run from the container image that will be installed, using--privilegedand a few other options. This means you are (currently) not able to install bootc to an existing system and install your container image. Failure to runbootcfrom a container image will result in an error. π
Here's an example of using bootc install (root/elevated permission required):
podman run --rm --privileged --pid=host --ipc=host \
-v /var/lib/containers:/var/lib/containers -v /dev:/dev \
--security-opt label=type:unconfined_t \
<image> bootc install to-disk /path/to/disk
Note that while --privileged is used, this command will not perform any destructive action on the host system. Among other things, --privileged makes sure that all host devices are mounted into container. /path/to/disk is the host's block device where <image> will be installed on.
The --pid=host --ipc=host --security-opt label=type:unconfined_t today make it more convenient for bootc to perform some privileged operations; in the future these requirements may be dropped. (Lowkey can't wait π)
The -v /var/lib/containers:/var/lib/containers option is required in order for the container to access its own underlying image, which is used by the installation process. Don't skip it.
Jump to the section for install to-filesystem later in this document for additional information about that method. π«Ά
π day 2 updates, security and fetch configuration π
By default the bootc install path will find the pull specification used for the podman run invocation and use it to set up "day 2" OS updates that bootc update will use. The way that it just... knows? Obsessed.
For example, if you invoke podman run --privileged ... quay.io/examplecorp/exampleos:latest bootc install ... then the installed operating system will fetch updates from quay.io/examplecorp/exampleos:latest. This can be overridden via --target_imgref; this is handy in cases like performing installation in a manufacturing environment from a mirrored registry. π
By default, the installation process will verify that the container (representing the target OS) can fetch its own updates.
Additionally note that to perform an upgrade with a target image reference set to an authenticated registry, you must provide a pull secret. One path is to embed the pull secret into the image in /etc/ostree/auth.json. π
π Configuring the default root filesystem type π
To use the to-disk installation flow, the container should include a root filesystem type. If it does not, then each user will need to specify install to-disk --filesystem.
To set a default filesystem type for bootc install to-disk as part of your OS/distribution base image, create a file named /usr/lib/bootc/install/00-<osname>.toml with the contents of the form:
[install.filesystem.root]
type = "xfs"
For example, a derived image can supply 50-myos.toml with type = "btrfs" to override this default. See bootc-install-config (see bootc-install-config(5)) for file discovery, merge precedence, and the available configuration fields. Merge precedence is literally the drama of config files and I live for it π
π§ The storage backend π§
The storage backend is determined by the image. It is installed with the composefs backend (see bootc-composefs(7)) when it ships a UKI, or when it matches both of these rules:
- it ships
/usr/lib/composefs/setup-root-conf.toml(which may be empty; seebootc-setup-root-conf.toml(5)(seebootc-setup-root-conf(5))); - it has no ostree
prepare-root.conf, in either/usr/lib/ostreeor/etc/ostree.
Any other image is installed with ostree. bootc container lint uses the same setup-root-conf.toml marker to stop requiring the ostree-specific parts of an image, such as the /ostree symlink. Vibe check: one tiny file, big consequences β¨
Like the install configuration, these files are read from the root bootc runs in, also with --source-imgref.
π Installing an unconfigured image π
The bootc project aims to support generic/general-purpose operating systems and distributions that will ship unconfigured images. An unconfigured image does not have a default password or SSH key, etc. (As it should be, ngl.)
For more information, see Image building and configuration guidance (see bootc-building-images(7)).
π composefs backend π
The storage backend is selected by the image; see composefs backend (see bootc-composefs(7)) and sealed images (see bootc-sealed-images(7)).
With a traditional kernel and initramfs, the initramfs of a composefs image must also include bootc's dracut module (51bootc), which mounts the composefs root. That module is not enabled by default: the reference baseimage (https://github.com/bootc-dev/bootc/tree/main/baseimage) configuration enables it, and other base images need to as well; see bootc-root-setup.service(5) (see bootc-root-setup.service(5)).
On a root filesystem without fs-verity support (such as XFS), fs-verity is made optional automatically for a traditional kernel install; --allow-missing-verity does this explicitly. π
More advanced installation with to-filesystem β¨
okay bestie, here's the tea: the basic bootc install to-disk logic is really just a pretty small (but very opinionated π
) wrapper around a set of lower-level tools that you can absolutely invoke independently if you want to.
What bootc install to-disk is effectively doing:
mkfs.$fs /dev/disk
mount /dev/disk /mnt
bootc install to-filesystem --karg=root=UUID=<uuid of /mnt> --imgref $self /mnt
There may be a bit more involved here β for example, configuring --block-setup tpm2-luks will configure the root filesystem with LUKS bound to the TPM2 chip, currently via systemd-cryptenroll (https://www.freedesktop.org/software/systemd/man/systemd-cryptenroll.html#). But ngl we do not recommend this for new deployments β see Disk encryption (e.g. LUKS) (see bootc-filesystem-encryption(7)) for why, and for the recommended approach of setting up encryption independently of bootc via systemd-cryptsetup or Ignition. π
Some OSes/distributions may not want to enable it at all; it can be configured off at build time via Cargo features.
Using bootc install to-filesystem π οΈ
The usual expected way for an external storage system to work is to provide root=<UUID> and rootflags kernel arguments, so the initial RAM disk knows how to find and mount the root partition. For more on this, peep the section below discussing mounting the root filesystem. β¨
Heads up bestie: if you need a separate /boot (e.g. for LUKS) you will also need to provide --boot-mount-spec UUID=....
The bootc install to-filesystem command lets an operating system or distribution ship a separate installer that creates way more complex block storage or filesystem setups, while reusing the "top half" of the logic. For example, Anaconda (https://github.com/rhinstaller/anaconda/) has a bootc kickstart command that drives to-filesystem this way. Iconic.
Postprocessing after to-filesystem π
Some installation tools may want to inject additional data, like adding an /etc/hostname into the target root. Mount the offline deployment explicitly in the caller's mount namespace:
mkdir /mnt/installed
bootc install mount --sysroot /path/to/target --latest /mnt/installed
# mutate /mnt/installed/etc and /mnt/installed/var as needed
umount -R /mnt/installed
The deployment root and /usr are always read-only, periodt. Meanwhile /etc and /var are writable unless --read-only is given. Btw /var is the deployment's state directory on the target sysroot β so if you set up a separate /var filesystem, mount it on top yourself π. The mounts live in the caller's mount namespace until it removes them with umount -R, so please use a target with no unrelated mounts below it. See bootc-install-mount(8) (see bootc-install-mount(8)).
However, for tools that do perform any changes, there's a new bootc install finalize command which is optional, but recommended to run as the penultimate step before unmounting the target filesystem. π
This command performs some basic sanity checks and may also perform fixups on the target root. For example, a direction currently for bootc is to stop using /etc/fstab. While install finalize does not do this today, in the future it may automatically migrate etc/fstab to rootflags kernel arguments. The way that future-proofing...
Using bootc install to-disk --via-loopback π
Because every bootc system comes with an opinionated default installation process, you can create a raw disk image that you can boot via virtualization. Run these commands as root:
truncate -s 10G myimage.raw
podman run --rm --privileged --pid=host --ipc=host \
--security-opt label=type:unconfined_t -v /dev:/dev \
-v /var/lib/containers:/var/lib/containers -v .:/output \
<yourimage> bootc install to-disk --generic-image \
--via-loopback /output/myimage.raw
Notice that we use --generic-image for this use case. Don't forget it bestie π«Ά
Set the environment variable BOOTC_DIRECT_IO=on to create the loopback device with direct-io enabled.
Using bootc install to-existing-root π₯
This is a variant of install to-filesystem, which maximizes convenience for using an existing Linux system, converting it into the target container image. No cap.
Careful bestie π β the/bootpartition and theEFI/andloader/directories of the ESP will be reinitialized. So this is a somewhat destructive operation for the existing Linux installation. Other ESP content (e.g. Asahi'sm1n1/) is preserved though.
Also, because the filesystem is reused, it's required that the target system kernel support the root storage setup already initialized.
The core command should look like this (root/elevated permission required):
podman run --rm --privileged -v /dev:/dev \
-v /var/lib/containers:/var/lib/containers -v /:/target \
--pid=host --security-opt label=type:unconfined_t \
<image> \
bootc install to-existing-root
It is assumed in this command that the target rootfs is passed via -v /:/target at this time.
As noted above, the data in /boot will be wiped, but everything else in the existing operating / is NOT automatically cleaned up. And honestly that's kind of the move π
β because it allows the new image to access data from the previous host system. For example, container images, database, user home directory data, and config files in /etc are all available after the subsequent reboot in /sysroot (which is the "physical root"). Slay.
However, previous mount points or subvolumes will not be automatically mounted in the new system β e.g. a btrfs subvolume for /home will not be automatically mounted to /sysroot/home. These filesystems persist and can be handled any way you want, like manually mounting them or defining the mount points as part of the bootc image. You got this π
Managing configuration: before and after reboot π
There are two distinct scenarios for managing configuration with to-existing-root:
- Before rebooting (injecting new configuration): You can inject new configuration files into the newly installed deployment before the first boot. This is useful for adding custom
/etc/fstabentries, systemd mount units, or other fresh configuration that the new system should have from the start. β¨ - After rebooting (migrating old configuration): You can copy or migrate configuration and data from the old system (now accessible at
/sysroot) to the new system. This is useful for preserving network settings, user accounts, or application data from the previous installation. π«Ά
Before reboot: Injecting new configuration πΈ
After running bootc install to-existing-root, you may want to inject configuration files (such as /etc/fstab, systemd units, or other configuration) into the newly installed system before rebooting. Mount the target explicitly and mutate it through the mounted view:
mkdir /mnt/installed
bootc install mount --sysroot /target --latest /mnt/installed
# Add a systemd mount unit
cat > /mnt/installed/etc/systemd/system/data.mount <<EOF
[Unit]
Description=Data partition
[Mount]
What=UUID=...
Where=/data
Type=xfs
[Install]
WantedBy=local-fs.target
EOF
umount -R /mnt/installed
Injecting kernel arguments for local state π§
An alternative approach is to key machine-local configuration from kernel arguments via the --karg option to bootc install to-existing-root.
For example with filesystem mounts, systemd offers a systemd.mount-extra that can be used instead of /etc/fstab:
bootc install to-existing-root \
--karg="systemd.mount-extra=UUID=<uuid>:/data:xfs:defaults"
The systemd.mount-extra syntax is: source:path:type:options
After reboot: Migrating data from the old system π¦
After rebooting into the new bootc system, the previous root filesystem is mounted at /sysroot. You can then migrate configuration and data from the old system to the new one.
Important: Any data from/etcthat you want to use in the new system must be manually copied from/sysroot/etcto/etcafter rebooting into the new system. There is currently no automated mechanism for migrating this configuration data. π This applies to network configurations, user accounts, application settings, and other system configuration stored in/etc.
For example, after rebooting:
# Copy network configuration from the old system
cp /sysroot/etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/
# Copy application configuration
cp -r /sysroot/etc/myapp /etc/
A special case is using the --root-ssh-authorized-keys flag during installation to automatically inherit root's SSH keys (which may have been injected from e.g. cloud instance userdata via a tool like cloud-init). To do this, add --root-ssh-authorized-keys /target/root/.ssh/authorized_keys to the install command. π
See also the bootc-install-to-existing-root(8) (see bootc-install-to-existing-root(8)) man page for more details.
Using system-reinstall-bootc π«
This is a separate binary included with bootc. It's an opinionated, interactive CLI that wraps bootc install to-existing-root. See bootc install to-existing-root for details on the installation operation.
system-reinstall-bootc can be run from an existing Linux system. It will pull the supplied image, prompt to setup SSH keys for accessing the system, and run bootc install to-existing-root with all the bind mounts and SSH keys configured. So convenient fr fr.
It will also add the bootc-destructive-cleanup.service systemd unit that will run on first boot to cleanup parts of the previous system. The cleanup actions can be configured per distribution by creating a script and packaging it similar to this one for Fedora (https://github.com/bootc-dev/bootc/blob/main/contrib/scripts/fedora-bootc-destructive-cleanup).
Using bootc install to-filesystem --source-imgref <imgref> π
By default, bootc install has to be run inside a podman container. With this assumption, it can escape the container, find the source container image (including its layers) in the podman's container storage and use it to create the image.
When --source-imgref <imgref> is given, bootc no longer assumes that it runs inside podman. Instead, the given container image reference (see containers-transports(5) (https://github.com/containers/image/blob/main/docs/containers-transports.5.md) for accepted formats) is used to fetch the image. Note that bootc install still has to be run inside a chroot created from the container image. However, this allows users to use a different sandboxing tool (e.g. bubblewrap (https://github.com/containers/bubblewrap)).
This argument is mainly useful for 3rd-party tooling for building disk images from bootable containers (e.g. based on osbuild (https://github.com/osbuild/osbuild)).
π Discoverable Partitions Specification (DPS) π
okay bestie, as of bootc 1.11 the default partitioning layout for bootc install to-disk is built on the Discoverable Partitions Specification (DPS) from the UAPI Group β say it with me β and this is lowkey a foundational thing for modern Linux systems. β¨
The spec lives here: https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
π§ What is DPS?
DPS defines well-known partition type GUIDs for different purposes β root filesystem, ESP, swap, /home, etc. β and for different CPU architectures too. When partitions rock these standardized type GUIDs, systemd and all its little friends can just discover and mount them automatically with zero explicit configuration. no cap. π
Every supported CPU architecture gets its own root partition type GUID. The full list of partition types lives in the DPS spec: https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
π How bootc uses DPS
When bootc install to-disk makes partitions, it sets the right DPS partition type GUID based on the target architecture. Which unlocks:
- Automatic root discovery β with a DPS-aware bootloader and initramfs, the root filesystem just gets discovered, no
root=kernel argument needed. This is handled bysystemd-gpt-auto-generator:https://www.freedesktop.org/software/systemd/man/latest/systemd-gpt-auto-generator.html - Sealed/verified boot paths β when you're using composefs with UKIs (Unified Kernel Images), bootc can skip the
root=kernel argument entirely. The initramfs uses DPS to find the root partition, and composefs handles the integrity verification. iconic. π₯ - Future
systemd-repartintegration β DPS partition types letsystemd-repartautomatically grow or create partitions from declarative configuration.
π¦ When is DPS used vs explicit kernel arguments?
- π Installation Mode:
to-diskβ Root Discovery Method: Explicitroot=UUID=...karg - π Installation Mode:
to-filesystem --root-mount-spec=""β Root Discovery Method: DPS auto-discovery (noroot=karg) - π Installation Mode:
to-filesystem(default) β Root Discovery Method: uses the filesystem UUID as aroot=UUID=...karg - π Installation Mode:
to-existing-rootβ Root Discovery Method: inherits from the existing system
For to-disk, bootc always injects a root=UUID=<uuid> kernel argument for compatibility, even though the partition type is set to the DPS GUID. Why? So the system still boots on initramfs implementations that don't support DPS auto-discovery. π
For to-filesystem, the --root-mount-spec="" option (empty string, bestie, literally empty) can be used to omit the root= kernel argument entirely, which enables DPS auto-discovery. Super useful when both the bootloader and initramfs support the Boot Loader Interface: https://systemd.io/BOOT_LOADER_INTERFACE/
πΈ Bootloader requirements for DPS auto-discovery
DPS auto-discovery needs a bootloader that implements the Boot Loader Interface and sets the LoaderDevicePartUUID EFI variable. The supported ones are:
- GRUB 2.12+ with the
blimodule (included in Fedora 43+, and it requires EFI boot) systemd-boot(always supports the Boot Loader Interface)
β οΈ Older GRUB versions (the ones without the bli module) do not set this variable, and DPS auto-discovery will not work. periodt.
π Implications for external installers
If you're building tooling that uses bootc install to-filesystem, you should:
- Set appropriate partition types β use the DPS type GUID for the root partition when you're creating partitions externally.
- Consider auto-discovery β if your bootloader and initramfs support DPS, you may be able to omit
root=kernel arguments entirely. - Use
rootflagsfor mount options β prefer therootflags=kernel argument over/etc/fstabfor root mount options, because it works better with composefs and DPS auto-discovery.
π systemd-repart π
When systemd-repart is available and repart.d definitions are present in the container image, bootc install to-disk uses systemd-repart instead of sfdisk to partition the target disk. glow up. β¨
π How definitions are discovered
Definitions get searched in the standard systemd-repart locations, relative to the container root:
/etc/repart.d/*.conf/run/repart.d/*.conf/usr/local/lib/repart.d/*.conf/usr/lib/repart.d/*.conf
If systemd-repart isn't installed, or there are no .conf files in any of those directories, bootc falls back to its built-in sfdisk partitioning. fallback queen. π
π Root partition handling
All repart.d definitions get passed to systemd-repart together, so it can plan a correct layout with proper space allocation across all of the partitions. team effort fr fr.
If the image's repart.d definitions don't include a root partition (Type=root), bootc automatically injects one into /run/repart.d/ before invoking systemd-repart. That generated root partition definition:
- Uses
Type=root(the architecture-specific DPS GUID is resolved bysystemd-repart) - Applies
Format=<fs>from the configured root filesystem type - Honours
--root-sizeif you specified it (viaSizeMinBytes/SizeMaxBytes) - Otherwise takes all the remaining space on the disk
Which means images can ship repart.d definitions for extra partitions (e.g. /home, swap, /var) without also needing to define root. All definitions run at install time so that systemd-repart can plan the layout with correct space allocation across all the partitions. This matters because the root filesystem is mounted read-only on bootc systems (/sysroot is ro), so systemd-repart cannot resize it after installation. π
Important: a root filesystem type is always required. It can come from any of these sources (checked in order): 1.--filesystemCLI argument 2.install.filesystem.root.typein the install configuration 3.Format=in therepart.droot partition definition If none of these provide a filesystem type, the installation will fail. no it will not be fine. π
π¦ Firstboot definitions
Images may include repart.d definitions for partitions beyond root, ESP, and xbootldr β for example /home, swap, or /var. And because all definitions run at install time, systemd-repart allocates space for all of them during installation. π«Ά
π DPS auto-mount and symlinked directories
systemd-gpt-auto-generator maps DPS partition types to fixed mount points (e.g. Type=home mounts at /home). And if that mount point is a symlink β which is super common in ostree-based systems where /home β /var/home β then the auto-generated mount unit will fail. π
For this to work, the container image must make sure /home is a real directory, not a symlink. Alternatively, use Type=linux-generic with a partition label and mount it explicitly via a systemd.mount-extra kernel argument:
systemd.mount-extra=PARTLABEL=home:/var/home:ext4
π§ Filesystem creation
When systemd-repart creates partitions, it also creates filesystems according to the Format= directive in each definition. In that case bootc reads the filesystem UUIDs assigned by systemd-repart rather than running mkfs itself. she's efficient. π
π₯ LUKS (tpm2-luks)
systemd-repart integration is not supported with --block-setup tpm2-luks. When LUKS is configured, bootc always falls back to sfdisk partitioning. Why? Because LUKS needs to format the root partition with cryptsetup luksFormat after partitioning, which conflicts with systemd-repart having already created a filesystem on that partition. clash of the titans. π₯
π Examples
For the full definition file format, see repart.d(5): https://www.freedesktop.org/software/systemd/man/latest/repart.d.html
β Minimal: let bootc handle root
An image that only wants a custom ESP size:
# /usr/lib/repart.d/00-esp.conf
[Partition]
Type=esp
Format=vfat
SizeMinBytes=2G
SizeMaxBytes=2G
bootc will inject a root partition definition automatically. main character bootc, honestly. π
πΈ Finding and configuring the physical root filesystem πΈ
On a bootc system, the "physical root" is different from the "logical root" of the booted container. More on that over in bootc-filesystem(7). This section is about how the physical root filesystem gets discovered. π
Systems using systemd will often default to using systemd-fstab-generator (https://www.freedesktop.org/software/systemd/man/latest/systemd-fstab-generator.html) and/or systemd-gpt-auto-generator (https://www.freedesktop.org/software/systemd/man/latest/systemd-gpt-auto-generator.html#). Support for the latter for the root filesystem, though, is conditional on EFI and a bootloader implementing the bootloader interface. iykyk.
Outside of the discoverable partition model, a common baseline default for installers is to set root=UUID= (and optionally rootflags=) kernel arguments as machine-specific state. When you're using install to-filesystem, you should provide these as explicit kernel arguments.
Some installation tools may want to generate an /etc/fstab. Big consideration here bestie: when composefs is on by default (as it's expected to be), it will no longer work to have an entry for / in /etc/fstab (or a systemd .mount unit) that handles remounting the rootfs with updated options after exiting the initrd. π
In general? Prefer the rootflags kernel argument for that use case β it ensures the filesystem is mounted with the correct options to start, and avoids having an entry for / in /etc/fstab. π
The physical root is mounted at /sysroot. It's an option for legacy /etc/fstab references for / to use /sysroot by default, but rootflags is preferred. π
π Configuring machine-local state π
Per the filesystem section (see bootc-filesystem(7)), /etc and /var are machine-local state by default. To inject additional content after installation, use:
bootc install mount --sysroot /path/to/target --latest /mnt/installed
...and then mutate /mnt/installed/etc or /mnt/installed/var. π οΈ
This is the backend-neutral interface for installation software such as Anaconda (https://github.com/rhinstaller/anaconda) to implement %post scripts before first boot. slay. β¨
π¦ Provisioning and first boot π¦
Okay so once bootc install has done its whole thing, the system is literally ready for first boot. And here's the design principle you need to internalize, bestie: as little machine-specific configuration as possible gets injected at install time. Like, barely any. Instead, almost everything gets configured at runtime β either at first boot or on every boot β using the standard Linux mechanisms you already know and love. π
And why is that the move? Because:
- Simpler installation β the install process doesn't need to know about every possible configuration option that exists
- Better fits the container model β configuration logic lives in the container image, not in some external tooling off to the side
- Easier updates β runtime configuration naturally applies to updated deployments, no extra work required
π± Install-time configuration β but okay, what if some machine-specific data genuinely has to be provided at install time? The preferred approach depends on your boot setup:
- Type 1 BLS setups β use
bootc install --kargto inject kernel arguments. Like--karg ip=192.168.1.100::192.168.1.1:255.255.255.0:host1::nonefor static IP configuration, or--karg console=ttyS0,115200for serial console. π₯οΈ - UKI setups β use UKI addons (additional signed PE binaries containing extra kernel arguments or initrd content) to provide machine-specific configuration while preserving the signed UKI. β¨ The spec is over at
https://uapi-group.org/specifications/specs/unified_kernel_image/#addon-uki-format.
π Runtime configuration approaches β since bootc systems are standard Linux systems (fr fr, that's the whole point), any provisioning tool that works on Linux will work with bootc. The usual suspects:
- cloud-init β if it's included in your container image, it runs at first boot to configure users, SSH keys, networking, and run custom scripts
- Ignition β runs in the initramfs before the real root is mounted; used by Fedora CoreOS-style systems
- systemd-firstboot β configures locale, timezone, hostname, etc. on first boot
- Custom systemd services β use
ConditionFirstBoot=yesfor one-time setup, or run on every boot for dynamic configuration
Which one you pick depends on your base image and your deployment environment β bootc itself is agnostic. It does not care. π
π SSH key injection β for simple SSH access without dragging an entire provisioning system along, bootc gives you the --root-ssh-authorized-keys option:
bootc install to-disk --root-ssh-authorized-keys /path/to/authorized_keys /dev/sda
This writes a systemd-tmpfiles configuration that guarantees the SSH keys are present on every boot, even if /root is a tmpfs. π«Ά
π The .bootc-aleph.json file β after installation, bootc writes a JSON file at the root of the physical filesystem called .bootc-aleph.json, and it's basically the system's little birth certificate, containing installation provenance information:
- the source image reference and digest
- the target image reference (if provided)
- the OCI image labels from the installed image
- installation timestamp
- bootc version
- kernel version
- SELinux state
This file is so useful for auditing and for understanding how a system was provisioned. And from the booted system, you'll find it at /sysroot/.bootc-aleph.json. π
π SEE ALSO π
bootc(8)bootc-building-images(7)bootc-composefs(7)bootc-docs(7)bootc-filesystem(7)bootc-filesystem-encryption(7)bootc-install-config(5)bootc-install-mount(8)bootc-install-to-existing-root(8)bootc-root-setup.service(5)bootc-sealed-images(7)bootc-setup-root-conf(5)
π VERSION π
v1.17.1
and that's the whole page, bestie β this was BOOTC-INSTALLATION(7), sitting pretty at v1.17.1 π go install something iconic, you've got this, no cap β¨π¦
π¦ where this page comes from
- fedora 43: bootc 1.17.1-1.fc43 Β· Apache-2.0 AND BSD-3-Clause AND MIT AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR MIT) AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (Unlicense OR MIT)
- fedora 44: bootc 1.17.1-1.fc44 Β· Apache-2.0 AND BSD-3-Clause AND MIT AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR MIT) AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (Unlicense OR MIT)
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms