Rolling back bootc systems

In this lab, you’ll look at rollbacks and examine how bootc handles directories on updates.

Rolling back changes to the virtual machine

You should still be logged into the bootc VM (the prompt should look like ` [core@localhost ~]$`), if not log back in now.

ssh core@qcow-vm

We have a new built-in option available to image mode systems that typically takes more preparation with package mode operations: the rollback.

Since bootc manages state on disk, we have the previous working system available to us. Normally, we’d need to have set up snapshots or refer to a backup but bootc automatically provides us the rollback built in.

Let’s check for an available rollback option to get us back to the previous image.

sudo bootc status
No staged image present
Current booted image: node.cxdrb.cxdrb.gcp.redhatworkshops.io/httpd
    Image version: 9.20250326.0 (2025-04-04 14:28:06.536999208 UTC)
    Image digest: sha256:737be173b45120c062473e9a88d020bc33c32a9069d4c257b74e4f5ab7241e41
Current rollback image: node.cxdrb.cxdrb.gcp.redhatworkshops.io/httpd (1)
    Image version: 9.20250326.0 (2025-04-04 14:11:45.808515934 UTC)
    Image digest: sha256:1e69b8a519eccf248e235960dd5eef3c5c546226c0000c655126e00e11438519
1 the on-disk deployment before our most recent update.

Looking at the status output, we can see the section marked rollback now has image information. There are at most 3 images available at any one time: staged for next deployment, the current booted deployment, and a rollback of the last active deployment. If for some reason you needed to go back further, you can deploy directly from the registry instead.

Rollbacks are as simple as running one command. Let’s get this image back to the previous state then we can dig into what happened.

sudo bootc rollback
bootfs is sufficient for calculated new size: 0 bytes
Next boot: rollback deployment

You will also find the value of rollbackQueued in the full YAML output has been updated as well. This can be useful to check before restarting a system. By adding the --format flag to the bootc status command we can get more details about the various images and operating state.

sudo bootc status --format yaml | grep Queued
  rollbackQueued: true

You can also check the boot order in the spec block to see what has been sent to the bootloader.

sudo bootc status --format yaml | grep Order
  bootOrder: rollback

Feel free to explore the full output of bootc status --format yaml on your own.

After the reboot, the rollback image will become the booted image, and will also become the new default in the boot order. Before doing so, please make sure you are logged in to the virtual machine and not the hypervisor, the prompt should look like [core@localhost ~]$.

sudo systemctl reboot

How image mode handles directories

On the lab host, we’re going to make some changes to the Containerfile to account for how directories are managed by bootc.

In an image mode system, bootc manages available images on disk for updates and rollbacks. You just created an update, applied it, then returned to a previous version all through bootc. Behind the scenes, bootc handles the following directories differently, which is what allows for the seamless update and rollback experience.

  • /usr → image state, contents of the image will be extracted and overwrite local files

  • /etc → local configuration state, contents of the image are merged with a preference for local files

  • /var → local state, contents of the image will be ignored after the initial installation of any image

The configuration files we added to /etc showed up after the update as a result of the merge treatment of bootc. If we’d made local changes that conflicted, we’d see the local changes rather than the new configs.

Our initial web page went in /var which means after it was unpacked from the original image, bootc treated it as local machine state. So the change in the Containerfile wasn’t applied to the running host. Since we want to control everything about our webserver from the image, we’ll need to make some changes to where we put content and how we serve it in Apache.

FROM registry.redhat.io/rhel9/rhel-bootc:9.5

RUN dnf install -y httpd

ADD etc/ /etc

RUN <<EOF (1)
    set -euxo pipefail
    mv /var/www /usr/share/www
    sed -i 's-/var/www-/usr/share/www-' /etc/httpd/conf/httpd.conf
EOF

RUN echo "Hello Red Hat Summit 2025!!" > /usr/share/www/html/index.html

RUN systemctl enable httpd.service
1 Runs several commands using the heredoc format instead of using the & operator to join multiple commands

Let’s break down that new RUN directive.

The httpd package drops content in /var/www by default, and on bootc systems /var is machine local. For this example, to manage web content via the image, we need to move it to somewhere under bootc control. We move the default package contents to a new location in /usr then update the Apache configuration to serve pages from this new directory.

Since the heredoc support for containerfiles essentially reads it as a mini-bash script, using set -euxo pipefail will show what’s run and cause any errors in these commands to bubble up and stop the build. This way we don’t need to wait to catch an error on the host to start troubleshooting.

We’ve also changed the echo line to create the index.html in the new docroot location.

Rebuild the image with our new configuration and index page. Since this is a rebuild, podman will reuse the existing layers if there are no changes. This makes updates faster and take less space. Notice the push to the registry also only pushes those layers that contain changes.

podman build --file Containerfile --tag {registry_hostname}/httpd

And make sure to push it to the registry:

podman push {registry_hostname}/httpd

Updating the virtual machine

Now you can ssh into the virtual machine

ssh core@qcow-vm

Previously, we checked for an update, downloaded and staged it locally to be activated, then manually rebooted the system to have the update take effect. This is a very good procedure for a manual update or in places where we need to schedule any outages ahead of time, say during a maintenance window. We can do this all at once by adding a flag to the update command. This gives us a way to automate the process, like with a systemd timer. Image mode hosts ship with this timer by default.

systemctl list-timers bootc-fetch-apply-updates.timer
NEXT                   LEFT          LAST PASSED UNIT                   ACTIVATES
Wed 2024-07-24 16:13:… 1h 44min left -    -      bootc-fetch-apply-upd… bootc-fetch-apply-upd…

1 timers listed.
Pass --all to see loaded but inactive timers, too.

Instead of waiting for this timer to trigger, we can immediately apply the new update and reboot.

Since we rolled back to an image that did not include our drop-in file for sudo, we will be prompted for our password. The drop-in file doesn’t have any local changes, so updates (and presence) are based on the image deployed, like our new web page. This is another thing to keep top of mind when moving between images, especially during a rollback.

sudo bootc update --apply
layers already present: 69; layers needed: 3 (6.2 kB)
Fetched layers: 6.10 KiB in 17 seconds (373 B/s)
  Deploying: done (4 seconds)                                                                         Queued for next boot: node.cxdrb.cxdrb.gcp.redhatworkshops.io/httpd
  Version: 9.20250326.0
  Digest: sha256:fe4feb4238bf1601df67b7feb04a9f1cf6fab208431af6ebd564fad5a3b7a637
Total new layers: 72    Size: 1.2 GB
Removed layers:   3     Size: 185.1 MB
Added layers:     5     Size: 185.1 MB
Rebooting system
Shared connection to qcow-vm closed.

Remember that the update will detail what layers are new, removed, or added, but this time will immediately reboot.

Testing the changes

We can check for our new web page from the lab host (`[lab-user@bastion ~]$ `):

curl http://qcow-vm

Now the output should be "Hello Red Hat Summit 2025!!"