1 / 1
Feb 23

post by nobody-9240 on Feb 23

Preface

On the Universal Blue Discourse, I saw this very simple guide to creating your own bootc image that I wanted to share: Locally built, automatically updating custom bootc image - General - Universal Blue

Notably, this does not rely on something like Github Actions to build your image. So it’s more private, not reliant on the whims of Microsoft, and likely faster.

Warning

Warning: this is unofficial and you should only do this if you are aware of the risks.

Before doing anything, it’s a good idea to pin your existing version.

# if this fails, try 1 instead sudo ostree admin pin 0

My version of the guide

To start, create a containerfile. Here’s an example of one based on mine.

# This is located at /etc/system-image - DO NOT INCLUDE THIS LINE FROM quay.io/fedora/fedora-silverblue:44 RUN --mount=type=bind,source=scripts,target=/tmp/scripts \ --mount=type=cache,destination=/var/cache/libdnf5 \ --mount=type=cache,destination=/var/lib/dnf5 \ --mount=type=tmpfs,destination=/var/log \ <<EOF # bash safety options set -euox pipefail # this containerfile mounts ./scripts to /tmp/scripts so that you don't # have to put everything in this containerfile bash /tmp/scripts/myscript.sh # remove packages dnf5 remove -y gnome-software # enable repos dnf5 copr enable -y scottames/ghostty # install packages dnf5 install -y fastfetch ghostty zsh zsh-autosuggestions zsh-syntax-highlighting EOF RUN bootc container lint

Now, we create a systemd service to build that containerfile into an image.

# This is located at /etc/containers/systemd/system.build - DO NOT INCLUDE THIS LINE [Build] Arch=amd64 ImageTag=localhost/system-image Pull=newer SetWorkingDirectory=/etc/system-image PodmanArgs=--squash [Service] ExecStartPost=/usr/bin/bootc switch --quiet --transport=containers-storage localhost/system-image:latest ExecStartPost=/usr/bin/bootc update --quiet ExecStartPost=/usr/bin/podman image prune --force --filter=label=containers.bootc=1 Nice=0

We will also want a timer so that systemd will automatically build new images.

# This is located at /etc/systemd/system/system-build.timer - DO NOT INCLUDE THIS LINE [Unit] Description=Daily automatic builds for custom bootc image [Timer] OnCalendar=daily OnBootSec=15min [Install] WantedBy=timers.target

Now, run the following command so systemd finds these files.

sudo systemctl daemon-reload

You can build and switch to the image by running

sudo systemctl start build system-build.service

You can watch the status of the build by running

sudo systemctl status system-build.service

And enable the timer for it by running

sudo systemctl enable --now system-build.timer

For my convenience, I store these 3 files in the same directory in my home. I then use this script to copy them to their proper places (works on a new installs and updates existing installs).

#!/bin/bash # bash safety options # -e exits on failure # -u exits on unknown variables # -o pipefail exits on failed pipe set -euox pipefail # Parse arguments RUN_NOW=false for arg in "$@"; do case $arg in --now) RUN_NOW=true shift ;; esac done # regardless pwd when running, cd into this script's directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # move Containerfile to its destination sudo mkdir -p /etc/system-image sudo cp "$SCRIPT_DIR/Containerfile" /etc/system-image/ sudo cp -r "$SCRIPT_DIR/scripts" /etc/system-image/ # move system.build to its destination sudo mkdir -p /etc/containers/systemd sudo cp "$SCRIPT_DIR/system.build" /etc/containers/systemd/ # move system-build.timer to its destination sudo mkdir -p /etc/systemd/system sudo cp "$SCRIPT_DIR/system-build.timer" /etc/systemd/system/ # run daemon-reload so systemd creates system-build.service sudo systemctl daemon-reload # enable and start the timer schedule sudo systemctl enable --now system-build.timer # conditionally start the service and tail logs if [ "$RUN_NOW" = true ]; then # trigger the build service to run immediately sudo systemctl start system-build.service --no-block # display the logs (control + C to exit) journalctl -u system-build.service -f fi

The script has two modes:

  • ./update which just copies the updated files to their system locations and makes systemd aware of the changes. This waits until the next scheduled run of the build timer.
  • ./update --now which copies the files and builds the image immediately.