ADR 9 — Artefact builder
Status: proposed
Context
Issue #11 points out two facts.
Every installable artefact that Corium produces (the qcow2, the raw disk and the
installer ISO) goes through bootc-image-builder. And upstream archived that
project on 2026-06-18. These are the facts, checked against the registries and the
upstream source, not against the announcement:
mise.tomland both workflows trackquay.io/centos-bootc/bootc-image-builder:latest. Its last build was on 2026-06-18 at 11:31 UTC, from commita686afe, “readme: migration notice”, which is the archive commit. Its digest issha256:2b52843ea2bfda73b0a08d97e76b734393b1d3a804681b9fabb26723bd3a2f0b. It is built on Fedora 42, which reached end of life on 2026-05-13, a month before the image was built. The tag that we follow stopped moving three months ago. Nobody noticed, because nothing broke.- The code did not stop. Upstream merged it into
osbuild/image-builder. That project releases weekly (v78.0.0throughv83.0.0between mid-August and 2026-09-16). It publishes three images from one workflow, all on Fedora 44.ghcr.io/osbuild/image-builderandghcr.io/osbuild/image-builder-cliare the same digest under two names.ghcr.io/osbuild/bootc-image-builderholds the same binary, installed as/usr/bin/bootc-image-builder, with the old entry point, flags, and output layout. Each image has a tag per release. For the compatibility image,v83.0.0issha256:e7aadce6b3f5639cd47d83354791931ea219891a0d113c2fe74a0f0d352b165c. - The
deprecation notice
commits to keep that entry point for the life of RHEL 10, and to drop it in
RHEL 11. It says the same about the
anaconda-isoimage type: deprecated, removed in RHEL 11, with the container-based installers as the path forward.
The issue asked three questions. We answered each one from the image-builder
source at v83.0.0 and the documentation in its tree. The published pages
describe the image types, but they do not say what the types do.
Is image-builder build --bootc-ref a drop-in replacement?
For the qcow2 and the raw disk, yes in substance and no in detail. The
command becomes image-builder build --bootc-ref <image> qcow2. It runs from
ghcr.io/osbuild/image-builder, with the same privileged flags and the same
mount of the root container store. The container must already be in local
storage. The release workflow already does this today, because it pulls the
image first. The builder reads the root filesystem type from the bootc install
configuration of the container (/usr/lib/bootc/install/00-corium.toml, per
ADR 2). So --bootc-default-fs is not necessary. Two things differ:
- The output layout.
image-builderwrites<distro>-<type>-<arch>/<distro>-<type>-<arch>.<ext>. For a bootc input, this isbootc-based-qcow2-x86_64/bootc-based-qcow2-x86_64.qcow2.bootc-image-builderwritesqcow2/disk.qcow2.--output-dirand--output-namerestore today’s paths. Sodeploy/proxmox/and the publish step, which only consume files, do not need to change. - A warning during manifest generation stops the build, unless you pass
--ignore-warnings. This is a feature, because the release path must fail loudly. But it is a change in behavior to expect on the first run.
For the ISO, no. If you give the CLI a bootc reference, it refuses the type outright:
image type bootc "anaconda-iso" is not supported with image-builder,
please consider switching to "bootc-installer" or use bootc-image-builder
The comment above that check gives the reason. Without the mTLS plumbing that
the old tool set up, the behavior would differ in small ways. Upstream chose an
error over a quiet incompatibility. anaconda-iso is reachable only through the
compatibility entry point. In upstream’s own image definitions, that code path
(bootc_legacy_iso) carries the note “we want to get rid of it”.
Can bootc-generic-iso carry a custom kickstart?
Not the way [customizations.installer.kickstart] does today, because the
generic ISO reads no build configuration at all. In the source,
manifestForGenericISO is the only image function that never receives the
blueprint. The ISO that it produces is the container itself, converted to a
squashfs and booted live. Its GRUB menu comes from
/usr/lib/image-builder/bootc/iso.yaml inside the container. When that ISO
boots, it does what the container does when it boots. Upstream’s reference has
three parts: a fedora-bootc container with Anaconda installed by dnf, an
iso.yaml that boots it with inst.stage2=, and a kickstart at
/usr/share/anaconda/interactive-defaults.ks. The kickstart holds a single
bootc --source-imgref … --target-imgref … line.
--bootc-installer-payload-ref copies the image to install into the container
store of the squashfs. This is what makes the install work offline.
So the kickstart does not go away. It moves. It is no longer a build input that
the builder receives. It becomes a file in an installer container that Corium
must build and own: a second Containerfile beside the appliance and ZFS
variants. The RAID root procedure in
docs/raid.md is still an
Anaconda kickstart, with the same content and the same three manual steps. Only
its delivery changes. It must be tested again on two disks, because the earlier
test used the ISO type that this decision retires.
Two things follow that are easy to miss:
- Today’s ISO is unattended, because
bootc-image-builderships a default kickstart foranaconda-iso. That kickstart installs to the first disk with no prompts. In the generic ISO, we must write that default ourselves. It is a few lines, and the quick start anddeploy/proxmox/create-vm-iso.shdepend on it. bootc-installer, the other suggested type, is Anaconda-based and does honor the blueprint kickstart. It uses the samekickstart.New(customizations)call as the legacy path. But it also requires two containers.--bootc-refmust be an Anaconda installer environment, and the image to install goes in--bootc-installer-payload-ref. Upstream lists it under “Historical” and says to prefer the generic type. It needs the installer container in both cases, so it saves nothing.
Does pinning buy useful time?
Pinning the Quay digest gives reproducibility and nothing else. The tag is already frozen. A digest makes that explicit, and lets CI record which builder produced an artefact. The release workflow cannot do this today. A digest does not make the builder maintained, and the base of the builder is a Fedora release that no longer receives updates.
Pinning the compatibility image from image-builder gives the time we
need. It has the same entry point, the same flags, and the same output paths.
The people who wrote it keep them working on purpose, on a current base, with a
version number to bump. It still builds anaconda-iso. This is exactly what we
need while we rebuild the ISO on the generic type: one change that stops the
dependency from decaying, and that does not touch the install path that a
release is verified against.
Decision
Now: build with
ghcr.io/osbuild/bootc-image-builder, pinned to a release tag and its digest, everywhere the builder is named. These places aremise.toml, the release workflow and the end-to-end workflow. Same command, same three artefact types, same output files. TheBIBenvironment override stays. So if the new image misbehaves, the frozen Quay digest is a one-line fallback. A bump of the pin is a reviewable diff that contains the digest, asbuild/k0s.lockalready does for the k0s binary.Next, as its own change: the qcow2 and the raw disk move to
image-builder build --bootc-ref. They run fromghcr.io/osbuild/image-builder, pinned the same way.--output-dirand--output-namekeepoutput/qcow2/disk.qcow2andoutput/image/disk.raw. The end-to-end workflow builds a qcow2 and boots it, and that is the test. After this step, the compatibility image builds the ISO and nothing else.Then: an installer container, built as
bootc-generic-iso. It has three parts: aContainerfilethat derives from the Fedora bootc base with Anaconda, aniso.yaml, and a default kickstart. The kickstart installs the embedded Corium image to the first disk, unattended.--bootc-installer-payload-refpasses the image to install, by digest, as today. The RAID root procedure becomes a variant of that container, with a different kickstart file at build time. We test it again by removing a disk, as we did for the first one. The published ISO stays onanaconda-isountil the generic ISO has installed a node through the release verification list, including the Proxmox script and the RAID procedure. Not before, because bare metal is a documented path with one artefact, and this ISO is the only thing that exercises that path.
Step 1 is in this change. Steps 2 and 3 wait for their tests to pass, not for a date. RHEL 11 is the outer limit, not the schedule.
Consequences
Nothing changes today for a person who builds or installs Corium. The commands, the artefacts and their paths stay the same, and the published ISO installs the same way.
The builder becomes a pinned dependency, like the others. This is a small
recurring cost: a digest to bump, ideally with each Corium release. In exchange,
we know which bytes produced an artefact, and the release job can say so. Today
the release job prints the digest of whatever :latest was at that time.
Step 2 changes the command in the same two places. It carries a risk: a warning
that bootc-image-builder tolerated can become an error. The end-to-end workflow
exists to catch this kind of failure.
Step 3 adds a second container image to build and a verification item to the
release list. It also makes the installer ours. A file in this repository
defines what the ISO does at boot, not the defaults of the builder. A project
that documents a RAID root needs it there. We rewrite the kickstart section of
docs/raid.md when step 3 lands, not before.
Upstream warns that a bootc system installed through Anaconda fails
systemd-remount-fs.service at boot
(
bugzilla 2332319).
Today’s ISO also installs through Anaconda. So either this already happens and
is harmless, or it does not reproduce here. In both cases, we test it when we
test the new ISO, and we do not assume it.
Alternatives considered
Keep tracking quay.io/centos-bootc/bootc-image-builder:latest. It did not
move after the archive commit, and it will not move. A tag that is frozen today
and returns 404 tomorrow is the failure mode that the issue describes, found
during a release. Rejected.
Pin the Quay digest and stop there. This is reproducible and honest about
what built an artefact. Otherwise it is identical to the option above: an
end-of-life base and an unmaintained binary. We keep it as the fallback that the
BIB override uses, not as the decision.
Move everything to image-builder build --bootc-ref in one step. Not
possible. The CLI refuses anaconda-iso, and the replacement ISO needs an
installer container that does not exist yet. If we do the disks now and the ISO
later, we reach the same end state, with a test between the two steps.
Use bootc-installer rather than bootc-generic-iso for the ISO, because
it honors [customizations.installer.kickstart] and keeps docs/raid.md closer
to its current content. It still requires our own installer container, and
upstream marks it historical. It also ties the ISO to Anaconda, where the generic
type leaves the choice to the container. It saves nothing. Rejected.
Drop the ISO and publish only disks. The ISO is the documented path onto
bare metal, and the RAID root depends on it. deploy/proxmox/create-vm-iso.sh
and the appliance image both produce one. Rejected.