Fedora Atomic: A simple guide to creating a custom, local bootc image

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:43

RUN --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

# 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

# dnf cleanup
dnf5 clean all
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
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

# 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/

# 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

# trigger the build service to run immediately
sudo systemctl start system-build.service
2 Likes