Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Best practices for building bootable containers

February 26, 2025
Valentin Rothberg
Related topics:
ContainersDeveloper ProductivityGitOpsApplication modernization
Related products:
Image mode for Red Hat Enterprise LinuxRed Hat Enterprise LinuxRed Hat Enterprise Linux for Edge

Share:

    Image mode for Red Hat Enterprise Linux (RHEL) simplifies the process of building, deploying, and managing Red Hat Enterprise Linux as a bootable container. Development, operations, and solution providers can simply use the same container-native tools and techniques to manage applications, the underlying operating system (OS) and more. 

    Please follow our series of articles detailing the experience of using image mode. In these articles, we discussed the various use cases of image mode, creating automated CI/CD pipelines, managing containerized workloads, a full GitOps experience for sysadmins of RHEL, and how image mode facilitates building software appliances.

    In this article, I want to talk about best practices for building bootable containers that form the building blocks of image mode for RHEL. 

    Building bootable versus application containers

    Like ordinary application containers, you can build bootable containers by using existing container technologies such as Containerfiles using existing tooling, such as Podman, Docker or buildkit. You can also store the images on any container registry, such as Quay.io, Docker Hub, the GitHub Container Registry, or any internal container registry. Figure 1 compares application containers and bootable containers.

    Application containers vs bootable containers.
    Figure 1: A comparison of application containers vs bootable containers.

    Bootable containers are a natural evolution of container technologies. For over a decade, containers have evolved into an industry standard of bundling, shipping, and deploying applications. Bootable containers build on top of these existing technologies and extend containers to include the entire operating system and the Linux kernel to allow for a comprehensive container-native workflow and user experience.

    Using Containerfiles

    Containers are commonly built via Containerfiles, also known as Dockerfiles. Those files include all the information needed to build a container image, such as the base image, instructions to install software packages or to copy files from a Git repository, and much more (Figure 2).

    Containerfiles are the center of gravity for building bootable containers.
    Figure 2: Containerfiles are the foundation for building bootable containers.

    The workflows and tools for building bootable containers are essentially the same as application containers. I see the beauty in that, since we can benefit from and build on top of more than a decade of innovation, rock-solid tooling and community-driven best practices. However, I want to elaborate on several best practices that only apply to building bootable containers. Let’s get started.

    Best practices for linting

    We recommend running the following bootc container lint command as a final stage in a Containerfile. This command will perform a number of checks inside the container image and throw an error when there is an issue.

    FROM quay.io/fedora/fedora-bootc:41
    # Customization steps
    RUN bootc container lint

    Running the bootc linter prevents us from running into certain bugs and helps us keep the image and the content in good shape. The community continuously adds more checks to the command, but the following are the most common ones:

    • Check for multiple kernels in /usr/lib/modules. Only one kernel is supported per image, so the command would exit with a failure.
    • Make sure that the syntax of files in /usr/lib/bootc/kargs.d is correct. This is the place where we can specify additional kernel arguments for bootc images. You can find more information in the upstream documentation.
    • Various hygiene checks, such as non-UTF-8 filenames, checks on /etc and /usr/etc, unwanted logfiles in /var/log, and more.

    Hence, if you work with bootc base images, make sure to run bootc container lint in the last command of your Containerfile.

    GitHub Actions and disk space

    Containers are commonly built in automated CI/CD pipelines. In another article, we elaborated on how to create such pipelines for image mode for RHEL to enable a GitOps-style user experience and workflow. If you are building images with GitHub Actions, you may run into disk-space related issues. Since bootc images ship an entire operating system, they tend to be larger than ordinary application images. Hence, if you run into such disk issues, make sure to add the following lines to your GitHub Actions workflow files:

    # Based on https://github.com/orgs/community/discussions/25678
        - name: Delete huge unnecessary tools folder
            run: rm -rf /opt/hostedtoolcache

    Removing the files in /opt/hostedtoolcache will free up a considerable amount of disk space that the bootc image can consume and ultimately help the container build to succeed.

    Understanding /var

    As outlined in the documentation, /var is really meant for persistent and mutable machine-local data and state. That means that during an update, /var will not be touched even when the container image has content in /var. Except for /var and /etc, all directories are mounted read-only, which is something we need to take into account when moving workloads over to image mode.

    For instance, the httpd webserver wants to write data to /var/www at install time. The data is only meant to be read at run time and should be part of updates. Hence, we need to configure the container image accordingly by moving the directory to /usr/share/www. Here you can find an example Containerfile installing httpd and preparing the image.

    Invoking useradd

    Often, packaging scripts may invoke useradd. This can cause state drift when /etc/passwd is also locally modified on the system, and transient /etc is not in use. You can find more details on this state drift in the bootc documentation.

    If a user does not own any content shipped in /usr and it runs as a systemd unit, then it’s often a good candidate to convert to systemd DynamicUser=yes, which has numerous advantages. Using DynamicUser will also help take care of ownership and more.

    However, porting to DynamicUser=yes can be somewhat involved in complex cases. If the RPM contains files owned by the allocated user, but that content is only in /var/lib/somedaemon or /var/log/somedaemon; then often the best fix is to drop that content from the RPM (you can %ghost it to mark it as owned) and switch to creating it at run time via systemd-tmpfiles.

    You can also switch to creating the user via systemd-sysusers. At that point, you can also drop the %post from the RPM which allocates the user.

    When your package owns content shipped in /usr

    Sometimes a daemon wants to drop privileges but also wants to access its configuration state in /etc. For example, polkit does this in /etc/polkit-1/rules.d. One solution is to use systemd’s BindReadOnlyPaths= option to mount the source directory into the namespace of the daemon.

    If you run into the situation of depending on a setuid/setgid binary, then there is no solution other than statically allocating the user, which requires global coordination. If you are a package maintainer, you can officially request such static users as described in the Fedora docs.

    Embedding containers with Quadlets

    Running containerized workloads in systemd is a simple yet powerful means for reliable and rock-solid deployments. Podman has an excellent integration with systemd in the form of Quadlet. Quadlet is a tool for running Podman containers in systemd in an optimal and declarative way. Workloads can be declared in the form of systemd-unit-like files extended with Podman-specific functionality.

    Quadlets integrate perfectly with image mode. You can find more details and examples in our previous article on containerizing workloads on image mode for RHEL. Note that using Quadlets at boot time may delay the boot process when new application container images need to be downloaded. However, there is a solution for that using so-called logically-bound images which will be pre-fetched during an update. Embedding containers via Quadlets works very well.

    Summary

    Using image mode is a paradigm shift in working with RHEL hosts. First, we can make use of all the great tools from the cloud-native world to build, deploy, and manage our operating system. Second, we are dealing with an immutable OS where large parts of the system are mounted read only. This article explained how to navigate that space.

    Last updated: February 28, 2025

    Related Posts

    • Image mode for RHEL: 4 key use cases for streamlining your OS

    • bootc: Getting started with bootable containers

    • Containerizing workloads on image mode for RHEL

    • How to create CI/CD pipelines for image mode for RHEL

    • How image mode for RHEL simplifies software appliances

    • How to name, version, and reference container images

    Recent Posts

    • Create and enrich ServiceNow ITSM tickets with Ansible Automation Platform

    • Expand Model-as-a-Service for secure enterprise AI

    • OpenShift LACP bonding performance expectations

    • Build container images in CI/CD with Tekton and Buildpacks

    • How to deploy OpenShift AI & Service Mesh 3 on one cluster

    What’s up next?

    Download the Advanced Linux Commands cheat sheet. You'll learn to manage applications and executables in a Linux operating system, define search criteria and query audit logs, set and monitor network access, and more.

    Get the cheat sheet
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue