Getting Started with bootc on Fedora: Build and Deploy Bootable Container Operating Systems

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

Context & Problems Solved

Operating server clusters over time frequently leads to configuration drift. An Ansible playbook might succeed on one node but fail on another due to subtle differences in the underlying base packages. The Image-based OS model fundamentally resolves this issue by freezing the entire operating system into a single immutable artifact.

Previously, tools like Packer or Image Builder produced rather heavyweight images. bootc (Bootable Containers) brings the familiar container workflow down to the operating system level. You write a Containerfile, build it with podman build, and push it to GHCR or Quay.io. The server pulls that image and loads it directly into the kernel to boot bare-metal hardware or virtual machines.

The operating system now functions as an immutable image with clear semantic tags (such as v1.0.4). If an update causes a kernel issue, you can roll back to the previous working state with a single reboot.

Preparing the Environment

You need a Fedora Workstation or Server machine with Podman pre-installed to follow along:

1. Install bootc

# Install the bootc CLI
sudo dnf install -y podman bootc

# Verify the version
bootc --version

2. Pull the Image Builder

We use bootc-image-builder to convert the container image into a virtual disk (.qcow2, .raw, or .ami):

# Pull the official image builder
podman pull quay.io/centos-bootc/bootc-image-builder:latest

Detailed Configuration

1. Write a Containerfile to Define the OS

Create a project directory and a Containerfile. We will use the Fedora bootc base image, install administration tools, configure a user, and enable SSH:

FROM quay.io/fedora/fedora-bootc:40

# Install required packages in a single layer
RUN dnf -y install \
    tmux \
    htop \
    neovim \
    curl \
    git \
    openssh-server \
    && dnf clean all

# Enable SSH service on boot
RUN systemctl enable sshd

# Create an admin user and grant sudo privileges
RUN useradd -m -G wheel -s /bin/bash sysadmin && \
    echo 'sysadmin:AdminSecret123!' | chpasswd && \
    echo "%wheel ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel-nopasswd

# Configure SSH Public Key for the user
RUN mkdir -p /home/sysadmin/.ssh && \
    chmod 700 /home/sysadmin/.ssh && \
    echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG... user@workstation" > /home/sysadmin/.ssh/authorized_keys && \
    chmod 600 /home/sysadmin/.ssh/authorized_keys && \
    chown -R sysadmin:sysadmin /home/sysadmin/.ssh

2. Build the OS Container Image

The build process is identical to building a standard web application container:

podman build -t quay.io/myrepo/fedora-custom-os:v1.0 .

3. Export QCOW2 Virtual Disk for KVM / Proxmox

Run the container builder to generate a virtual disk file from the newly built image:

mkdir -p ./output

sudo podman run \
  --rm \
  --privileged \
  --security-opt label=type:unconfined_t \
  -v ./output:/output \
  -v /var/lib/containers/storage:/var/lib/containers/storage \
  quay.io/centos-bootc/bootc-image-builder:latest \
  --type qcow2 \
  --local \
  quay.io/myrepo/fedora-custom-os:v1.0

The disk export process takes about 2-3 minutes. The output produces the file ./output/qcow2/disk.qcow2 (around 2.2GB – 2.5GB), ready to be imported into Proxmox or virt-manager to spin up a VM.

Best Practices for Image Design

  • Partitioning scheme: The /usr partition is mounted read-only. Only /etc (for configuration) and /var (for logs, databases, container storage) are writable.
  • Image size optimization: Group all dnf install packages together and always include dnf clean all within the same layer to prevent unnecessary image bloat.
  • Separate build stages: Use multi-stage builds to compile Go/Rust binaries in the initial stage, then copy the clean binary artifacts into the final bootc base image.

Operations & Monitoring

1. Check Deployment Status

SSH into the bootc server and run the status check command:

sudo bootc status

The output will display the active image (staged/booted), the SHA256 digest, and the linked remote registry.

2. Upgrade the Operating System (In-place Upgrade)

After modifying the Containerfile and pushing tag v1.1 to the registry, you can update the OS with just two commands:

# Pull the new build into the staging area
sudo bootc upgrade

# Reboot to apply the new OS
sudo systemctl reboot

If you need to switch to a different base image (e.g., from the standard image to one with pre-installed Nvidia drivers):

sudo bootc switch quay.io/myrepo/fedora-nvidia-os:latest
sudo systemctl reboot

3. Rollback on Failure

If an update triggers a kernel panic or crashes networking services, revert immediately:

# Roll back to the previous deployment
sudo bootc rollback
sudo systemctl reboot

If the server loses network connectivity, you can select the previous kernel entry directly from the GRUB boot menu.

4. Automated Update Checks

Enable the built-in systemd timer to automatically check for new image updates on a schedule:

sudo systemctl enable --now bootc-fetch.timer
sudo systemctl list-timers | grep bootc

The timer silently pulls new layers in the background. The system will apply the update on the next reboot without disrupting running services.

Share: