Copr builders powered by bootc

17. 06. 2025 | Jakub Kadlčík | EN fedora copr bootc image-builder

As of today, all Copr builder virtual machines are now being spawned from bootc images, which is no small feat because the builder infrastructure involves multiple architectures (x86_64, aarch64, ppc64le, s390x), multiple clouds (Amazon AWS, IBM Cloud), and on-premise hypervisors. It scales up to 400 builders running simultaneously and peaking at 30k builds a day.

Before bootc

You can find some interesting history and previous numbers in Pavel’s article - Fedora Copr farm of builders - status of July 2021. The part it leaves out is how we used to generate the Copr builder images.

The process is documented in the official Copr documentation. In a nutshell, it involved manually spawning a VM from a fresh Fedora Cloud image, running Ansible playbooks to provision it, and then using custom scripts to upload the image to the right place. Because we need to build the images natively, we had to follow this process for every architecture.

The easiest workflow was for x86_64 builders running on our own hypervisors. It meant connecting to the hypervisor using SSH and running a custom copr-image script from the praiskup/helpers repository. While its usage looks innocent, internally it had to execute many virt-sysprep commands. It also required some guestfish hacks to modify cloud-init configuration inside of the image so that it works outside of an actual cloud. Then, finally, using the upload-qcow2-images script to upload the image into libvirt.

The same exact workflow for ppc64le builders. However, internally it had a special case uploading the image also to OSU OSL OpenStack.

For s390x builders, we don’t have a hypervisor where we could natively build the image. Thus we needed to spawn a new VM in IBM Cloud and run the previously mentioned copr-image script inside of it. Once finished, we needed to upload the image to IBM Cloud. This is supposed to be done using the ibmcloud tool, but the problem is that it is not FOSS, and as such, it cannot be packaged for Fedora. We don’t want to run random binaries from the internet, so we containerized it.

At this point, only x86_64 and aarch64 images for Amazon AWS remain.

While not straightforward to create a new AMI from a local qcow2 image, it’s quite easy to create an AMI from a running EC2 instance. That was our strategy. Spawn a new instance from a fresh Fedora Cloud image, provision it, and then create an AMI from it.

Current situation

I disliked exactly three aspects concerning the previous solution. It required a lot of manual work, the process was different for every cloud and architecture, and the bus factor was less than one.

Even though at this moment generating a fresh set of builder images still requires about the same amount of manual work as before, there is a potential for future automation. By switching to bootc and Image Builder, we were able to offload some dirty work to them while also unifying the process to follow the same steps for all architectures and clouds (with minor caveats).

The current process is temporarily documented here. We spawn a VM for every architecture and use it to build the system image from our Containerfile via the quay.io/centos-bootc/bootc-image-builder. Then we fetch the results and upload them where needed.

For Amazon AWS, we can utilize the image-builder upload feature which is amazing. But for other clouds and hypervisors, we still need our custom upload-qcow2-images and quay.io/praiskup/ibmcloud-cli. If image-builder could implement the missing support and enable uploading to all of them, that would be a major win for us.

Future plans

My goal is simple, I want one-button deployment. Well, almost.

When a change is made to our Containerfile, or when triggered manually, or periodically after a period of inactivity, I want the images to be automatically built for all architectures and uploaded to all the necessary places. Then seeing a list of image names and AMIs that I can either choose to use or ignore.

The bootc-image-builder-action seems like the perfect candidate, but the problem is that it cannot natively build images for ppc64le and s390x.

SNThrailkill recommended GitLab Runners but that would require us to maintain the runner VMs, which is annoying. Moreover, there is a potential chicken-and-egg problem, meaning that if we break our image, we might not be able to spawn a VM to build a new working image. We also wouldn’t be able to use the existing GitHub action and would have to port it for GitLab.

At this moment, our team is leaning towards Konflux and a tekton pipeline for building images. Fedora Konflux instance is limited to x86_64 and aarch64, so we would temporarily have to use an internal Red Hat instance which provides all the architectures needed by us.

Many questions are yet to be answered. Is Konflux ready? Does the pipeline for building images already exist? Does it support everything we need? Is it built on top of image-builder so that we can use its upload feature?

Pitfalls along the way

Hopefully, this can help Image Builder and bootc developers better understand their blind spots in the onboarding process, and also prevent new users from repeating the same mistakes.

Before discovering that bootc exists, our original approach was to use just Image Builder and its blueprints, and automatize the process using Packit. There were several problems. It was easy to build the image locally from our blueprint, but it wasn’t possible to upload the same blueprint to be built in a hosted Image Builder service. Additionally, I had several issues with the Blueprint TOML format. The order of operations is pre-defined (e.g. all users are always created before any packages are installed). There is no escape hatch to run a custom command. And finally, it’s yet another specification to learn. My recommendation? Just go with bootc.

Our main problem with bootc is the immutability of the filesystem. Can somebody please help me understand whether the immutable filesystem is a fundamental building block, a key piece of technology that enables bootable containers, or whether it is an unrelated feature? If it is technologically possible, our team would love to see officially supported mutable bootc base images. Currently, we are going forward with a hack to make the root filesystem transient.

One of the issues that probably stems out of the immutable filesystem is the necessity to change the default location of the RPM database. This hack is baked into the bootc base images and we needed to revert it because it causes Mock to fail under some specific circumstances. This unfortunately cost us many many hours of debugging.

The process of building system images is quite storage intensive in /var/lib/containers and /run. To avoid running out of disk space on our virtual machines, we had to turn our swap partition into a data volume and mount the problematic directories there. Not sure if there is something that image-builder can do to make this a less of problem.

We build the system images natively on VMs of the same architecture that they are targeted for, but then we fetch all of them to an x86_64 machine and upload the images to the respective clouds from there. We discovered a bug in cross-arch upload to AWS, which was promptly confirmed and fixed by the image-builder team. Big customer satisfaction right here.

We also struggled with setting up AWS permissions for the image-builder upload command to work correctly. We tried running it, fixing the insufficient permissions it complained about, running it again, and again, and so on. I don’t recommend this approach. It turns out there is a documentation page with instructions.

I hope this chapter doesn’t come across as too discouraging. In fact, we found workarounds for all of our problems, and we are now happily using this in production. So you can probably too.