Chapter 7. Creating bootc images from scratch


To create a bootc image from scratch, use an existing bootc base image as a build environment. This process takes user RPM packages as input. You must therefore rebuild the image if the RPM packages change. By creating bootc images from scratch, you can have control over the underlying image content and adjust your system environment to your requirements.

Building a minimal image allows you to reduce the system footprint and attack surface by including only the packages essential for your specific workload.

7.1. Using pinned content to build images

To maintain reproducible builds, you can use repository snapshot tools to pin a base image to specific package versions. This ensures that rpm-md or yum repositories remain consistent with the lockfiles of your project.

With the bootc image from scratch feature, you can configure and override package information in source RPM repositories, while referencing mirrored, pinned, or snapshotted repository content. Consequently, you gain control over package versions and their dependencies.

Prerequisites

  • A standard bootc base image.

Procedure

  • The following example creates a bootc image from scratch with pinned content:

    # Begin with a standard bootc base image that serves as a "builder" for our custom image.
    FROM registry.redhat.io/rhel10/rhel-bootc:latest as builder
    # Configure and override source RPM repositories, if necessary. The following step is required when referencing specific content views or target mirrored/snapshotted/pinned versions of content.
    RUN rm -rvf /etc/yum.repos.d; mkdir -p /etc/yum.repos.d/
    COPY mypinnedcontent.repo /etc/yum.repos.d/
    # The file must be copied to the /etc/yum.repos.d/ directory.
    # The mypinnedcontent.repo is your standard repository that ensures that your container will always be built with the exact same versions of software, preventing unexpected bugs or security issues from new updates.
    # Build the root file system using the specified repositories and non-RPM content from the "builder" base image.
    # If no repositories are defined, the default build will be used. You can modify the scope of packages in the base image by changing the manifest between the "standard" and "minimal" sets.
    # Add additional repositories to apply customizations to the image. However, referencing a custom manifest in this step is not currently supported without forking the code.
    RUN /usr/libexec/bootc-base-imagectl build-rootfs --manifest=standard /target-rootfs
    # Create a new, empty image from scratch.
    FROM scratch
    # Copy the root file system built in the previous step into this image.
    COPY --from=builder /target-rootfs/ /
    # Apply customizations to the image. This syntax uses "heredocs" https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/ to pass multi-line arguments in a more readable format.
    RUN <<EORUN
    # Set pipefail to display failures within the heredoc and avoid false-positive successful builds.
    set -xeuo pipefail
    # Install necessary packages, run scripts, etc.
    dnf -y install NetworkManager emacs
    # Remove leftover build artifacts from installing packages in the final built image.
    dnf clean all
    rm /var/{log,cache,lib}/* -rf
    EORUN
    # Define required labels for this bootc image to be recognized as such.
    LABEL containers.bootc 1
    LABEL ostree.bootable 1
    # Optional labels that only apply when running this image as a container. These keep the default entry point running under systemd.
    STOPSIGNAL SIGRTMIN+3
    CMD ["/sbin/init"]
    # Run the bootc linter to avoid encountering certain bugs and maintain content quality. Place this command last in your Containerfile.
    RUN bootc container lint

Verification

  1. Save and build your image.

    $ podman build -t quay.io/<namespace>/<image>:<tag> . --cap-add=all --security-opt=label=type:container_runtime_t --device /dev/fuse
  2. Build <_image_> image by using the Containerfile in the current directory:

    $ podman build -t quay.io/<namespace>/<image>:<tag> .

7.2. Building a base image up from minimal

For advanced image customization, you can generate a minimal bootc image derived from the standard base operating system image. This lightweight image contains only the bootc tool, the kernel, and the DNF package manager.

The minimal bootc image is designed to serve as a foundational layer for subsequent multi-stage builds. You can control the final image content.

Note

This minimal image is currently not shipped pre-built in the registry and must be generated locally.

Prerequisites

  • A standard bootc base image.

Procedure

  • The following example creates a custom minimal base image:

    # Begin with a standard bootc base image that is reused as a "builder" for the custom image.
    FROM registry.redhat.io/rhel10/rhel-bootc:latest as builder
    # Configure and override source RPM repositories, if necessary. This step is not required when building up from minimal unless referencing specific content views or target mirrored/snapshotted/pinned versions of content.
    # Add additional repositories to apply customizations to the image. However, referencing a custom manifest in this step is not currently supported without forking the code.
    # Build the root file system by using the specified repositories and non-RPM content from the "builder" base image.
    # If no repositories are defined, the default build will be used. You can modify the scope of packages in the base image by changing the manifest between the "standard" and "minimal" sets.
    RUN /usr/libexec/bootc-base-imagectl build-rootfs --manifest=minimal /target-rootfs
    # Create a new, empty image from scratch.
    FROM scratch
    # Copy the root file system built in the previous step into this image.
    COPY --from=builder /target-rootfs/ /
    # Apply customizations to the image. This syntax uses "heredocs" https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/ to pass multi-line arguments in a more readable format.
    RUN <<EORUN
    # Set pipefail to display failures within the heredoc and avoid false-positive successful builds.
    set -xeuo pipefail
    # Install required packages for our custom bootc image.
    # Note that using a minimal manifest means we need to add critical components specific to our use case and environment.
    dnf -y install NetworkManager openssh-server
    # Remove package caches
    dnf clean all
    # Clean up all logs and caches
    rm /var/{log,cache,lib}/* -rf
    # Run the bootc linter to perform build-time verification. Keep this as the last command in your build instructions.
    bootc container lint
    # Close the shell command.
    EORUN
    # Define required labels for this bootc image to be recognized as such.
    LABEL containers.bootc 1
    LABEL ostree.bootable 1
    # Optional labels that only apply when running this image as a container. These keep the default entry point running under systemd.
    STOPSIGNAL SIGRTMIN+3
    CMD ["/sbin/init"]

7.3. Building required privileges

Generating a root filesystem from scratch requires the inner build process to use some nested containerization (such as mount namespacing) that are not enabled by default.

Prerequisites

  • The container-tools meta-package is installed.

Procedure

  • Generate a new root file system, providing these arguments at a minimum to podman build:

    --cap-add=all --security-opt=label=type:container_runtime_t --device /dev/fuse

7.4. Generating your bootc images from scratch

Generate a custom minimal base image to establish a streamlined and secure foundation for your Red Hat Enterprise Linux applications. By creating a minimal base image, you can reduce the overall system footprint and attack surface by ensuring that only essential packages are included in the final deployment.

Prerequisites

  • The container-tools metapackage is installed.

Procedure

  • Create a Containerfile. The following is an example:

    # The following example reuses the default base image as a "builder" image. Optionally,  you can use the commented instructions to configure or override the RPM repositories in /etc/yum.repos.d to, for example, refer to pinned versions
    FROM registry.redhat.io/rhel10/rhel-bootc:latest as builder
    # RUN rm -rf /etc/yum.repos.d/*
    # COPY mycustom.repo /etc/yum.repos.d
    RUN dnf repolist && /usr/libexec/bootc-base-imagectl build-rootfs --manifest=minimal /target-rootfs
    # Create a new, empty image from scratch.
    FROM scratch
    # Copy the root file system built in the previous step into this image.
    COPY --from=builder /target-rootfs/ /
    # You can make arbitrary changes such as copying the systemd units and other tweaks from the baseconfig container image. This example uses the heredocs syntax, to improve and make it easy to add complex instructions, and install critical components
    RUN <<EORUN
    set -xeuo pipefail
    # Install networking support and SSH which are not in minimal
    dnf -y install NetworkManager openssh-server
    dnf clean all
    rm /var/{log,cache,lib}/* -rf
    bootc container lint
    EORUN
    # This label is required
    LABEL containers.bootc 1
    LABEL ostree.bootable 1
    # These labels are optional but useful if you want to keep the default of running under systemd when run as a container image.
    STOPSIGNAL SIGRTMIN+3
    CMD ["/sbin/init"]

Next steps

  • After creating your Containerfile, you get an image with a single tar file large layer. Every change, such as pushing to the registry, pulling for clients, results in copying the single large tar file, and increases the container image size. You can optimize the container image that you created for a smaller version.

Use the bootc-base-imagectl rechunk subcommand to optimize an input container imagery, splitting its file system into content-addressed reproducible layers, with precomputed SELinux labeling. This enables better network efficiency for both pushes and pulls by maximizing layer reuse (deduplication) and minimizing data transfer across image builds.

Prerequisites

  • You have a previously-built base image.

Procedure

  • Run the following command to rechunk your base image.

    $ sudo podman run --rm --privileged -v /var/lib/containers:/var/lib/containers \
          registry.redhat.io/rhel10/rhel-bootc:latest \
          /usr/libexec/bootc-base-imagectl rechunk \
              quay.io/exampleos/rhel-bootc:single \
              quay.io/exampleos/rhel-bootc:chunked

    The rechunk operation works on an image produced by the default mode of creating new images FROM <rhel-bootc>. It is useful in combination with the scratch builds that output only a single large tar layer.

    Without rechunk operation every change to the input, for example a kernel update, results in a new layer including the entire contents of the bootc image. This new layer must be pushed, stored by registries, and pulled by clients.

    The bootc-base-imagectl utility, included within bootc images, runs inside a container. However, it requires mapping the host’s containers-storage into the container to function.

Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat Documentation

Legal Notice

Theme

© 2026 Red Hat
Back to top