Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training

    Featured resources

    • Open Source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

How to deploy an image mode update in offline and air-gapped environments

August 13, 2025
Armando Ortiz
Related topics:
Linux
Related products:
Image mode for Red Hat Enterprise LinuxRed Hat Enterprise Linux

    The usual workflow for deploying image mode updates onto a host machine is dependent upon a network connection to access a registry and to obtain updates. However, for reasons involving security, location, or even hardware limitations, a system might end up needing an update when remote access isn't possible. Fortunately, image mode for Red Hat Enterprise Linux is flexible enough to maintain and update when deployed online, offline, and in air-gapped environments.

    Prerequisites

    For this article, I prepared my updates on a system running Fedora Workstation 42, and deployed the update to hardware running Red Hat Enterprise Linux 10 (RHEL). However, as long as you're able to create containers and have a system that supports image mode updates, the exact setup isn't important. 

    Here are the requirements for this workflow:

    • Podman
    • Skopeo
    • Access to a registry or a locally stored container
    • An external storage device for the container requiring an update 

    Benefits and disadvantages

    The method I demonstrate here is based on deploying updates on a device by device basis. While this is a perfectly functional method, it’s not ideal for all situations.

    Benefits

    • The machine you’re updating is able to be fully offline and airgapped

    Disadvantages

    • Can be a time consuming process when used across many devices
    • Requires someone on site capable of deploying the update

    If your system is capable of being online, then this approach is not the best option for you. Remote repositories are a great way to deploy updates to a system, and it doesn't require as much setup as this method does. If your situation doesn't match the benefits of this method, and you do have access to remote registries, read How to build, deploy, and manage image mode for RHEL to learn more about managing those systems.

    An overview of the process from start to finish:

    1. Prepare an external storage device on an online system
    2. Copy the image containing the updates you want to distribute to external storage
    3. Use the external storage device to apply updates to your offline system

    Prepare an external storage device on an online system

    One of the challenges of an offline update is obtaining the container you wish to use as the source of the update. In most cases, a container would be made on another system where testing can be done, and then distributed remotely for deployment. However, because this workflow assumes no internet or wireless connection, everything must be done with external storage devices rather than remote registries.

    Before you plug an external storage device into your online system, get a report about what storage devices are already connected to your system. Currently, you only care about the NAME column:  

    $ lsblk
    NAME          MAJ:MIN     SIZE   RO TYPE  MOUNTPOINTS
    zram0           251:0       8G    0 disk  [SWAP]
    nvme0n1         259:0   476.9G    0 disk  
    ├─nvme0n1p1    259:1     600M    0 part  /boot/efi
    ├─nvme0n1p2    259:2       1G    0 part  /boot
    └─nvme0n1p3    259:3   475.4G    0 part  

    Now plug in your external storage and run the same command. You can compare these two outputs to ensure that you know what your system calls your external storage device. In my case, the USB drive being used is named sda, and it has a partition called sda1.

    $ lsblk
    NAME        MAJ:MIN   SIZE   RO TYPE  MOUNTPOINTS
    sda             8:0   28.9G    0 disk  
    └─sda1         8:1   28.9G    0 part 
    zram0         251:0      8G    0 disk  [SWAP]
    nvme0n1       259:0  476.9G    0 disk  
    ├─nvme0n1p1  259:1    600M    0 part  /boot/efi
    ├─nvme0n1p2  259:2      1G    0 part  /boot
    └─nvme0n1p3  259:3  475.4G    0 part  

    The MOUNTPOINTS column lists the mount points of the partitions on your external storage. If your system mounts external storage automatically, then valid mount points already exist. However, if there are no mount points (as in my example), then you must mount it yourself before you can store anything on the device.

    Start with an empty directory. You can either create one, or use an existing empty directory that already exists for this purpose:

    $ sudo mkdir /mnt/usb/

    Once you've got an empty directory, you can mount your device partition. The mount command doesn't normally provide confirmation (only an error generates output). You can verify success by checking the mount point again (I've truncated the output for brevity):

    $ sudo mount /dev/sda1 /mnt/usb
    $ lsblk
    NAME         MAJ:MIN    SIZE   RO TYPE  MOUNTPOINTS
    sda              8:0   28.9G    0 disk  
    └─sda1          8:1   28.9G    0 part  /mnt/usb
    [...]

    Your external storage device is now ready for copying files onto it.

    Transfer an image to external storage

    You can now copy the container to your mounted device. For a container you've got stored locally, use the skopeo command (adapt the paths and names of the container for your own environment): 

    $ sudo skopeo copy --preserve-digests --all \
    containers-storage:localhost/rhel-container:latest \
    oci://mnt/usb/

    For a container stored on a remote registry:

    $ sudo skopeo copy --preserve-digests --all \
    example://quay.io/example:latest \
    oci://mnt/usb/

    Depending on the size of the container, these commands might take a few minutes to complete. Once the container has been copied, unmount and eject the external storage:

    $ sudo umount /dev/sda1
    $ sudo eject /dev/sda1

    Update the container on an offline system

    To apply the update, first plug the external storage device into your offline system. This might not be mounted automatically, so use the mkdir and mount commands as needed to locate the external storage and then mount it.

    For the sake of stability and reusability, it's best to copy the container from the external device over to the offline system's local container registry. Copy the container to the offline machine's local container storage:

    $ skopeo copy --preserve-digests --all \
    oci://var/mnt/usb \
    containers-storage:rhel-update:latest

    In this case, the mount point of the external storage is the path entered to the oci section, while the containers-storage section varies depending on the name and tag you wish the container to have. Use Podman to verify that your container is now local:

    $ podman images
    REPOSITORY			        TAG     IMAGE ID  CREATED  SIZE
    example.io/library/rhel-update   latest  cdb6d...  1 min    1.48 GB

    Deploy the update using bootc:

    $ bootc switch --transport containers-storage \
    example.io/library/rhel-update:latest

    If you weren't able to copy your container to local storage, then you must use the oci transport and the path to your storage device instead:

    $ bootc switch --transport oci /var/mnt/usb

    While it might seem to make more sense to use bootc upgrade, it's the --transport flag in bootc switch that enables you to specify an alternative source for the container. By default, bootc would attempt to pull from a registry because the bootc image builder used a registry to build the original image. There is no way to specify where an update is located when using bootc upgrade. By using bootc switch and specifying that you're using local container storage, this enables you to not only remove the requirement of a remote registry, but also to deploy updates by using  this local container in the future.

    After you’ve done this once, you can now successfully use bootc upgrade, as long as your local container and the update share the same location. If you want to switch to updates on a remote repository in the future, then you’d have to use bootc switch again. To ensure that the update was properly deployed, use the command bootc status:

    $ bootc status
    Staged image: containers-storage:example.io/library/rhel-update:latest
      Digest: sha256: 05b1dfa791...
      Version: 10.0 (2025-07-07 18:33:19.380715153 UTC)
    Booted Image: localhost/rhel-intel:base
      Digest: sha256: 7d6f312e09...
      Version: 10.0 (2025-06-23 15:58:12.228704562 UTC)

    The output shows your current booted image, along with any changes staged to happen. The container you used earlier is here, but note that staged changes do not occur until the next reboot. The output also confirms that updates will be pulled from your container storage.

    After a reboot, you can verify that you've booted into the correct image:

    $ bootc status
    Booted image: containers-storage:example.io/library/rhel-update:latest
    	Digest: sha256: 05b1dfa791...
    	Version: 10.0 (2025-07-07 18:33:19.380715153 UTC)
    Rollback image: localhost/rhel-intel:base
    	Digest: sha256: 7d6f312e09...
    	Version: 10.0 (2025-06-23 15:58:12.228704562 UTC)

    The Booted image is your updated image, and the Rollback image is your previous image. You've successfully performed an offline image mode update.

    If you're not using containers yet, and need help setting one up, read Image mode for Red Hat Enterprise Linux: A quick start guide. 

    Related Posts

    • How to build, deploy, and manage image mode for RHEL

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

    • Creating a VMDK using image mode for Red Hat Enterprise Linux

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

    • Containerizing workloads on image mode for RHEL

    • How image mode for RHEL simplifies software appliances

    Recent Posts

    • Red Hat UBI 8 builders have been promoted to the Paketo Buildpacks organization

    • Using eBPF in Red Hat products

    • How we made one data layer serve the UI, the mocks, and the E2E tests

    • Build trusted Python containers with Project Hummingbird and Calunga

    • Simplify distributed tracing: ObservabilityInstaller installation

    What’s up next?

    Use Podman Desktop to create a bootable Flask-based application using image mode for RHEL. We will integrate Flask, Gunicorn, and NGINX into a bootable container.

    Start the activity
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    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
    © 2026 Red Hat

    Red Hat legal and privacy links

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