Custom Fedora/CentOS bootc base container images
A core premise of the bootc model is that rich control over Linux system customization can be accomplished with a "default" container build:
FROM <base image>
RUN ...
As of recently, it is possible to e.g. swap the kernel and other fundamental components as part of default derivation.
This page is documenting a change which is on track to land in the CentOS images; but is not yet enabled in the Fedora base images. |
Goals
Exact content version control
However, some use cases want even more control - for example, as an organization deploying a bootc system, I may want to ensure the base image version carries a set of packages at exactly specific versions (perhaps defined by a lockfile, or an rpm-md repository). There are many tools which manage snapshots of yum (rpm-md) repositories.
There are currently issues where it won’t quite work to e.g.
dnf -y upgrade selinux-policy-targeted
.
Building up from minimal
Today there is just one default image produced by the project, which is now known as "standard". It is roughly a headless server-oriented installation (although it can be used for desktops as well), and comes with a lot of opinionated packages for networking, CLI tooling, etc.
This project also has a "minimal" content set definition which is not currently shipped as a distinct container, but can be built from the standard image.
Using /usr/libexec/bootc-base-imagectl
The /usr/libexec/bootc-base-imagectl
tool which is
included in the base image is designed to enable building
a root filesystem from an exact set of provided input RPMs.
Understanding the base image content
Most, but not all content from the base image comes from RPMs.
There is some additional non-RPM content, as well as postprocessing
that operates on the filesystem root. At the current time the
implementation of the base image build uses rpm-ostree
,
but this is considered an implementation detail subject to change.
Using bootc-base-imagectl build-rootfs
The core operation is bootc-base-imagectl build-rootfs
.
This command takes just one required argument:
-
A path to the target root filesystem which will be generated as a directory. The target should not already exist (but its parent must exist).
Additionally, --manifest
can be provided to choose the input set of packages
and configuration. The two default images are:
-
standard
: The default image. -
minimal
: A quite small root content set, effectivelybootc
,systemd
,kernel
,dnf
plus their hard dependencies, as well as a small set of non-RPM content tweaks to e.g. enable systemd persistent journaling.
For more information on the available content sets, run bootc-base-imagectl list
.
The set of packages is currently not officially supported to configure directly.
The general intention is that you can start from either image, and then add, change
or remove content from it in a secondary build phase. Especially building up from
the minimal
image should cover many use cases that want to control the content
set to a high degree.
A key goal is that this avoids "forking" by default, ensuring that by default when we change the base image (adding new packages usually), custom builds will inherit that change by default.
A "source root" can also be provided to enable "cross builds". More on this below.
Build privileges required
We’re generating a new root filesystem, not modifying the existing container. This is most easily done by using container features (mount namespacing in particular) that is not enabled by default in many container build environments today.
In summary, you must provide at least these arguments to e.g. podman build
:
--cap-add=all --security-opt=label=type:container_runtime_t --device /dev/fuse
It is a goal to reduce these required capabilities; see this tracker issue.
Note however ultimately the goal here is to deploy this container image as a base operating system on a physical or virtualized environment, where inherently more trust is required regardless.
Example: Generating a custom minimal base image
# This starts from the default base image, which we reuse as a "builder" image.
FROM quay.io/centos-bootc/centos-bootc:stream10 as builder
# Note here, you may want to configure/override the RPM repositories in
# /etc/yum.repos.d to e.g. refer to pinned versions. For example:
# RUN rm -vf /etc/yum.repos.d
# COPY mycustom.repo /etc/yum.repos.d78
# /etc/yum.repos.d/mycustom.repo
RUN /usr/libexec/bootc-base-imagectl build-rootfs --manifest=minimal /target-rootfs
# This container image uses the "artifact pattern"; it has some
# basic configuration we expect to apply to multiple container images.
FROM quay.io/exampleos/baseconfig@sha256:.... as baseconfig
# We start from nothing.
FROM scratch
# Fill the image with the root filesystem tree built in the previous step.
COPY --from=builder /target-rootfs/ /
# Now we make other arbitrary changes. Copy our systemd units and
# other tweaks from the baseconfig container image.
COPY --from=baseconfig /usr/ /usr/
# This syntax uses "heredocs" https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/
RUN <<EORUN
set -xeuo pipefail
# Install critical components
dnf -y install NetworkManager cowsay
dnf clean all
rm /var/{log,cache,lib}/* -rf
bootc container lint
EORUN
# This label is required
LABEL containers.bootc 1
# These labels are optional but useful if you want to keep the default of running under systemd
# when run as a container image.
STOPSIGNAL SIGRTMIN+3
CMD ["/sbin/init"]
Example: "Cross" builds
The simplest use case is building a new container image from the builder
container, where the OS major version of the builder container is the
same as the target system (e.g. using fedora-bootc:41
to generate a system
with Fedora 41 RPMs).
However, the tooling does support "cross" (operating system major) builds with the pattern of a separate "repos" container. This can be used to aid bootstrapping scenarios.
# This container defines the RPM configuration (releasever, yum repos, etc)
FROM quay.io/fedora/fedora-bootc:41 as repos
# Now the builder container can be a different OS major version.
FROM quay.io/centos-bootc/centos-bootc:stream10 as builder
RUN --mount=type=bind,from=repos,target=/repos,rw /usr/libexec/bootc-base-imagectl build-rootfs --manifest=minimal /repos /target-rootfs
FROM scratch
COPY --from=builder /target-rootfs/ /
# ... insert here arbitrary build steps, as above
LABEL containers.bootc 1
Optimizing container images
The output of the above will be an image with a single large layer (tarball), which means every change will result in copying (pushing to the registry, pulling for clients) the single large tarball.
There is support in rpm-ostree to take a large image like this an "rechunk" it.
This section is a stub that will be expanded.
Want to help? Learn how to contribute to Fedora Docs ›