Blog Post

Linux and Open Source Blog
9 MIN READ

Red Hat Enterprise Linux 10 Image Mode on Azure Quick Start Guide

abbottkarl's avatar
abbottkarl
Icon for Microsoft rankMicrosoft
May 19, 2025

 This guide will get you up and running with Image Mode on RHEL on Azure to help demonstrate how the technology works and how it can save you time and effort in managing your RHEL estate on Azure.

To get started, you’ll need the following:

  • Container Registry
  • Linux server for building containers
  • Azure Subscription for deploying a RHEL VM in Image Mode

On an existing Linux system with “az” installed (and logged into an Azure account), let’s go ahead and begin by creating a resource group:

az group create -l eastus -n linux-vms

Now on that same system, let’s generate a ssh key for logging into vms:

ssh-keygen -f ~/.ssh/linuxkey -t rsa

And hit enter at both passphrase prompts. This will generate an ssh key for the purpose of this quickstart. In production, you may wish to actually use a passphrase to better protect your ssh keys. 

Now, we need to create a container registry. To do this on Azure, let’s go to https://portal.azure.com/#create/Microsoft.ContainerRegistry and on the first screen, specify an active subscription and choose “linux-vms” as our resource group. 

For the name of the registry, it must be unique across the Azure namespace, so try something with your name. In my case, my name is Karl and so I’m going to use “rhel10demo” for the purpose of this quick start. Anywhere you see that name, please replace it with your own registry name.

Your first page should look similar to this when completed:

Once you’ve filled those values in, click “Review + Create” and then on the next screen, click “Create”. Once the deployment finishes, you will have a container registry ready to go.

Back over on our Linux system, let’s now deploy to Azure a Linux server for building containers:

az vm create \
  --resource-group linux-vms \
  --name container-builder \
  --image RedHat:rhel-raw:9_5:9.5.2024120516 \
  --admin-username core \
  --assign-identity \
  --ssh-key-values ~/.ssh/linuxkey.pub\
  --public-ip-sku Standard

Once this command has completed, you’ll have a public ip address specified as the value for “publicAddress” in the output from the above command. This document will use ip.ip.ip.ip in place of an actual ip address to show you where you’d place the value.

We also need to copy over our linuxkey.pub file to the container building machine so that we can inject this key into the container images we build:

scp -i ~/.ssh/linuxkey ~/.ssh/linuxkey.pub core@ip.ip.ip.ip:.ssh/

Now let’s get started building our image mode container image!

ssh core@ip.ip.ip.ip -i ~/.ssh/linuxkey

First, we want to install podman and git:

sudo dnf install -y podman git

Now we need to have the Azure CLI tooling so that we can interact with our Azure Container Registry, to do that, let’s run:

sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc

We now need to edit /etc/yum.repos.d/azure-cli.repo and make sure that it has the following contents:

[azure-cli]
name=Azure CLI
baseurl=https://packages.microsoft.com/yumrepos/azure-cli
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc

We can now run:

sudo dnf install azure-cli -y

Once this package has installed, we need to run:

az login

And follow the instructions.

Let’s now generate a token credential granting us registry access by doing the following: (Remember to replace “rhel10demo” with your own registry name.)

az acr token create --name mytoken --resource-group linux-vms --registry rhel10demo --scope-map _repositories_push 
  "creationDate": "2025-04-23T14:02:59.654151+00:00",
 "credentials": {
   "certificates": null,
   "passwords": [
     {
       "creationTime": "2025-04-23T14:03:10.774839+00:00",
       "expiry": null,
       "name": "password1",
       "value": "roF8leBAMlfeTS2Jb75WmaK/Z0WATcRK/sNowu6Str+ACRAZruef"
     },
     {
       "creationTime": "2025-04-23T14:03:10.774858+00:00",
       "expiry": null,
       "name": "password2",
       "value": "vzRJsZwsp4PX/ToXTmAFQitz2yYye+dqHZXbbUXZrD+ACRBPzPXi"
     }
   ],
   "username": "mytoken"
 },

In the output, we will see two passwords. Let’s grab password1, which in our example looks like: (You’ll want to save this information as it’s the only time you’ll get to see it!)

 

   "passwords": [
     {
       "creationTime": "2025-04-23T14:03:10.774839+00:00",
       "expiry": null,
       "name": "password1",
       "value": "roF8leBAMlfeTS2Jb75WmaK/Z0WATcRK/sNowu6Str+ACRAZruef"
     },

Here the password is the value of the “value” parameter, and we can now log into the registry with the following command: (Remember to replace “rhel10demo” with your registry name.)

podman login rhel10demo.azurecr.io -u mytoken -p roF8leBAMlfeTS2Jb75WmaK/Z0WATcRK/sNowu6Str+ACRAZruef

Next, we want to clone the git repository that has our MSSQL Image Mode example:

git clone https://github.com/mrguitar/rhel-mssql-bootc

As we are using Azure PAYG (Pay as you Go) images, we need to enable RHUI access inside of containers by running:

sudo sh -c "echo -e '/usr/share/rhel/secrets:/run/secrets\n/etc/pki/rhui:/etc/pki/rhui\n/etc/yum.repos.d/rh-cloud-base.repo:/etc/yum.repos.d/rh-cloud-base.repo' >> /etc/containers/mounts.conf"
sudo chmod 644 /etc/pki/rhui/{private,product}/*

Now, let’s change into the directory of our git repository and copy our auth.json for our container:

cd rhel-mssql-bootc
mkdir -p etc/ostree
cp /run/user/1000/containers/auth.json ./etc/ostree/

We can now examine the Containerfile.azure and see that it has instructions to build and bring up a system. In the from container, we already have MIcrosoft SQL Server installed, so we do not have to add that to our container:

FROM quay.io/mrguitar/rhel-mssql-bootc:latest
COPY etc/ /etc/
COPY 05-cloud-kargs.toml /usr/lib/bootc/kargs.d/
ARG sshpubkey

RUN if test -z "$sshpubkey"; then echo "must provide sshpubkey"; exit 1; fi; \
   useradd -G wheel core && \
   mkdir -m 0700 -p /home/core/.ssh && \
   echo $sshpubkey > /home/core/.ssh/authorized_keys && \
   chmod 0600 /home/core/.ssh/authorized_keys && \
    chown -R core: /home/core

# install required packages and enable services

RUN dnf -y install \
       WALinuxAgent \
       cloud-init \
       cloud-utils-growpart \
       gdisk \
       hyperv-daemons && \
   dnf clean all && \
   systemctl enable NetworkManager.service && \
   systemctl enable waagent.service && \
   systemctl enable cloud-init.service && \
   echo 'ClientAliveInterval 180' >> /etc/ssh/sshd_config

# configure waagent for cloud-init to handle provisioning

RUN sed -i 's/Provisioning.Agent=auto/Provisioning.Agent=cloud-init/g' /etc/waagent.conf && \
   sed -i 's/ResourceDisk.Format=y/ResourceDisk.Format=n/g' /etc/waagent.conf && \
   sed -i 's/ResourceDisk.EnableSwap=y/ResourceDisk.EnableSwap=n/g' /etc/waagent.conf

We can now build the container image by running:

podman build --build-arg "sshpubkey=$(cat ~/.ssh/linuxkey.pub)" --no-cache -f Containerfile.azure

Now that we have our container registry configured and our image built, we can push the image to the registry. Let’s start by finding the image ID for our newly built image:

[core@container-builder rhel-mssql-bootc]$ podman images
REPOSITORY                         TAG         IMAGE ID      CREATED        SIZE
<none>                             <none>      ba4f948305db  2 minutes ago  3.64 GB
quay.io/mrguitar/rhel-mssql-bootc  latest      534ac925516a  4 weeks ago    3.47 GB

We can now tag this image with the following command (Remember to replace “rhel10demo” with your registry name.):

podman tag ba4f948305db rhel10demo.azurecr.io/mssql-image-mode:azure

Now we can push our image to our registry:

podman push rhel10demo.azurecr.io/mssql-image-mode:azure

Now, let’s exit from the container building server and go create a new RHEL 9.5 VM that we will switch from package mode to image mode. This needs to be run on the system that you created the container building image on.

az vm create \
  --resource-group linux-vms \
  --name mssql \
  --image RedHat:rhel-raw:9_5:9.5.2024120516 \
  --admin-username core \
  --assign-identity \
  --ssh-key-values ~/.ssh/linuxkey.pub \
  --public-ip-sku Standard

Once this machine starts, let’s ssh to the system:

ssh -i ~/.ssh/linuxkey core@ip.ip.ip.ip

Let’s start by installing podman:

sudo dnf install podman -y

Now we must login to our container registry using sudo so that podman running as root can download our container image: (We will use the credentials that were generated earlier in this quick start.)

sudo podman login rhel10demo.azurecr.io -u mytoken -p roF8leBAMlfeTS2Jb75WmaK/Z0WATcRK/sNowu6Str+ACRAZruef

and then we can run the following command to pull down our image mode image and deploy it to this system:

sudo podman run --privileged --pid=host -v /var/lib/containers:/var/lib/containers -v /:/target -v /home/core/.ssh/authorized_keys:/bootc_authorized_ssh_keys/root rhel10demo.azurecr.io/mssql-image-mode:azure bootc install to-existing-root --acknowledge-destructive --root-ssh-authorized-keys /bootc_authorized_ssh_keys/root

At this point, when the above command completes successfully, you can run:

sudo systemctl reboot

And when you reboot, your RHEL system will now be running in Image Mode! Let’s go ahead and log back in and see what’s different:

ssh -i ~/.ssh/linuxkey core@ip.ip.ip.ip

Let’s start by trying to create a file in /opt as root – something you could normally do on package mode RHEL:

sudo touch /opt/hello.txt

If you are on an image mode system, you’ll see this output:

touch: cannot touch 'hello.txt': Read-only file system

This is because the files in the container image are immutable and the only way to change them is to change the image and reboot into the image! This provides a significant new layer of security in that operating system files cannot be easily modified. You are still able to edit files in /etc and in home directories.

This image has Microsoft SQL Server installed and we can verify this by running:

sudo systemctl status mssql-server

You should see output similar to:

○ mssql-server.service - Microsoft SQL Server Database Engine
    Loaded: loaded (/usr/lib/systemd/system/mssql-server.service; disabled; preset: disabled)
    Active: inactive (dead)
       Docs: https://docs.microsoft.com/en-us/sql/linux

Given that we want all virtual machines based on this image to have SQL Server running by default, let’s make that change to the container image. To do this, we’ll need to exit this shell and ssh into the container builder machine from earlier.

On that machine, run:

cd rhel-mssql-bootc

And then use this command to add “RUN systemctl enable mssql-server.service” to the end of Containerfile.azure:

echo >> Containerfile.azure && echo "RUN systemctl enable mssql-server.service" >> Containerfile.azure

Once this is done, we need to rebuild the image, tag the rebuilt image, and push the new image to our container registry: (Remember to replace “rhel10demo” with your registry name.)

podman build --build-arg "sshpubkey=$(cat ~/.ssh/linuxkey.pub)" --no-cache -f Containerfile.azure

podman images
REPOSITORY                                 TAG         IMAGE ID      CREATED         SIZE
<none>                                     <none>      b7322184d64e  9 seconds ago   3.67 GB
rhel10demo.azurecr.io/mssql-image-mode  azure       237947455bd9  53 minutes ago  3.67 GB
quay.io/mrguitar/rhel-mssql-bootc          latest      e36c92d89714  4 days ago      3.5 GB


podman tag b7322184d64e rhel10demo.azurecr.io/mssql-image-mode:azure
cp etc/ostree/auth.json /run/user/1000/containers
podman push rhel10demo.azurecr.io/mssql-image-mode:azure

Once this is done, we can exit this machine and log back on to our image mode machine and run the following:

sudo bootc upgrade
sudo systemctl reboot

And watch the magic as the box reboots! 

Now when you ssh in again, run:

sudo systemctl status mssql-server

This time, you should get output showing that SQL Server is running. Upon updating and rebooting all VMs based on this image, they’ll now be running SQL Server.

At this point, we can run the SQL Server demo by running:

sudo PATH=$PATH:/opt/mssql/bin/ /opt/mssql_demo.sh

This demo shows that the SQL Server is working on our RHEL machine running in Image Mode.

Any further changes that you want to make to this box can be pushed by changing the container image in the registry and calling a bootc upgrade.

Image Mode RHEL also ships with a timer that allows these boxes to check for updates on the weekend. This can be configured so that not all of your image mode RHEL boxes update at the same time. 

With Image Mode RHEL, you don’t have to worry about system drift as you are always running off of a known image. Also, you’ll probably find yourself saving a lot of time by not having to log on to lots of systems. If you need to spin up 1,000 VMs with the same image, you can easily do that with the tooling we’ve shown you today on top of Azure!

Published May 19, 2025
Version 1.0
No CommentsBe the first to comment