Packaging for Bootc Images
Recently bootable containers (aka bootc) have gained significant interest for deploying and reproducibly build linux systems. Projects like ublue use this technology to build on top of standard Fedora images and add stuff on top. Luckily, they also make it easy for anybody to create your own image further downstream (see their Image template)
What is the problem?
As mentioned, this all builds on top of Fedora and their package repos. Any package contained within these repos can be easily added to your own image. However, this changes if it is not packaged. Where to get them then?
The Hard Way - COPR
The Fedora project provides a service to build your own packages on copr which can be easily added using the dnf package manager. However, this process is not that straight forward and the documentation is a bit lacking and requires to build rpms. Many packages are also already present in the copr-repos - but sometimes they are badly maintained and its hard to find the correct one.
My approach - Containers for the win
As these bootable containers are defined using Containerfiles (aka Dockerfiles), a straight-forward approach would be to make use of containers to also build these packages. This is exactly what I have done. I defined Containerfiles to build the packages I wanted to include in my image and then, as part of the assembly of the bootable container, I just copied the packages from the pre-built containers. Thanks to modern languages such as Go and Rust, the built binaries can be standalone binaries making it easy to just build and copy them.
Let me give you an example:
Let’s say I want to build lazygit
and include it in my image.
I define a Containerfile for just building lazygit
.
FROM golang AS lazygit-builder
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
WORKDIR /usr/src/app
RUN set -ex && \
mkdir -p /buildapp && \
export GOPATH=/buildapp && \
go install github.com/jesseduffield/lazygit@v0.52.0
Here, I just make use of the basic golang image.
If I would require some libraries present and want to ensure that it works for Fedora, I would start with fedora:latest
, install golang into it, and start from there.
Then I can use GitHub Actions to build and push this to my repo as, e.g., myospkgs-lazygit
.
So this will build a container with the lazygit binary.
How to get the binary to your image? Just add the following to your Containerfile for the bootable container:
FROM ghcr.io/username/myospkgs-lazygit:latest as lazygit-builder
# Template from ublue...
# Allow build scripts to be referenced without being copied into the final image
FROM scratch AS ctx
COPY build_files /
# Base Image
FROM ghcr.io/ublue-os/bazzite:stable
# ...
# stuff is happening
# ..
# copy your binary over
COPY --from=lazygit-builder /buildapp/bin/lazygit /usr/bin/lazygit
# continue to do stuff ...
And with this you now have your own freshly build and up-to-date lazygit
package included in your system.
By adding another GitHub Action and Dependabot definition for lazygit you can also easily keep it up-to-date and rebuild every time a new version is released.
Yey!
This might not be the Fedora-blessed way, but if you are making use of containers anyway I found this much easier to wrap my head around :)
Why not just have it in one file?
Yes, you could also just build the package with every image you build. However, this increases the build time and creates unnecessary addtional CPU time costing energy. Therefore, I found it more elegant to split in separate builds and combine them on the fly.