Ol
Operating Systems Azure

Oracle Linux 9 Arm64 LVM on Azure User Guide

| Product: Oracle Linux 9 Arm64 LVM on Azure

Overview

This guide covers deploying and operating Oracle Linux 9 on 64-bit Arm (AArch64/arm64) with an LVM disk layout on Microsoft Azure, using cloudimg's pre configured virtual machine image from the Azure Marketplace.

Oracle Linux 9 is Oracle's free, freely redistributable enterprise Linux distribution, binary compatible with Red Hat Enterprise Linux 9. This is the native Arm build, running the Unbreakable Enterprise Kernel (UEK). It runs on Azure's Arm virtual machine sizes, which are powered by the Azure Cobalt 100 processor (the Dpsv6 and Dplsv6 families), where many workloads deliver the same performance at a lower cost than on x86.

No application is preinstalled: the operating system itself is the product. Its root filesystem lives on an LVM logical volume rather than a fixed partition, so you can grow the root volume onto a larger disk, add volumes and reshape storage on a running machine with standard LVM tools.

What's included:

  • Oracle Linux 9, native 64-bit Arm (arm64 / AArch64) with a 4k page size, binary compatible with RHEL 9
  • The Unbreakable Enterprise Kernel, Oracle's supported kernel for Oracle Linux 9 on Arm
  • An LVM disk layout: volume group rootvg with the root filesystem on rootlv and a dedicated crashlv for kernel crash dumps
  • Every available update applied at build time, and again immediately before capture
  • Automatic updates armed to apply all available updates, never rebooting on their own
  • Azure Linux Agent (waagent) and cloud-init for Azure integration, Chronyd synchronised against the Azure host clock
  • SELinux in enforcing mode and firewalld active, as Oracle ships it
  • SSH as the only network listener, with no swap on the operating system disk
  • Gen2 Hyper V virtual machine support
  • 24/7 cloudimg support

Platform: Microsoft Azure (Gen2 Hyper V, Arm64, Azure Cobalt 100) Default user: azureuser

This is the Oracle Linux 9 major release, on Arm

This listing tracks the Oracle Linux 9 major release, not a frozen minor. The image is built from whichever Oracle Linux 9 point release is current at build time and brought fully up to date, and its update repositories are Oracle's rolling Oracle Linux 9 channels, so your own dnf update keeps rolling the machine forward within Oracle Linux 9. Oracle Linux 9 has Premier Support until June 2032 and Extended Support until June 2035.

Because this is the Arm build, it is a separate product from the x86 Oracle Linux 9 images: it carries aarch64 packages and an aarch64 kernel, and it only runs on Arm virtual machine sizes.

Security posture

This image ships with no known credential. There is no default password and no baked in SSH key:

  • The root account password is locked, so no one can log in as root with a password.
  • No account on the image has a usable password at all.
  • PasswordAuthentication and keyboard interactive authentication are disabled in the SSH daemon, so every login is by key.
  • Root cannot log in with a password over SSH, and no key is installed for root. Use sudo from azureuser instead.
  • SSH host keys are removed before capture and regenerated on each machine at first boot, so no two machines you launch share a host identity.
  • /etc/machine-id and the NetworkManager secret key are cleared before capture and regenerated per machine.
  • The only key that works is the public key you supply when you create the virtual machine. Azure injects it into the administrator account you name at first boot.
  • SELinux is left in enforcing mode and firewalld is active, the Oracle Linux defaults.

Prerequisites

Before deploying this image, ensure you have:

  1. An active Microsoft Azure subscription
  2. Access to the Azure Portal or Azure CLI
  3. An SSH key pair for Linux VM access
  4. Familiarity with Azure VM management

Recommended VM Size: Standard_D2ps_v6 (2 vCPU, 8 GB RAM, Azure Cobalt 100) or larger. This is an Arm64 image, so it must be launched on an Arm virtual machine size, such as the Dpsv6 or Dplsv6 families. An x86 size such as Standard_B2s will not accept this image.

Step 1: Deploy the Virtual Machine

Option A: Azure Portal

  1. Navigate to the Azure Marketplace and search for "Oracle Linux 9 Arm64 LVM cloudimg"
  2. Select the image and click Create
  3. Configure the basics:
  4. Subscription: Select your Azure subscription
  5. Resource Group: Create new or select existing
  6. Virtual Machine Name: Enter a name for your VM
  7. Region: Select a region that offers Arm sizes
  8. Size: Standard_D2ps_v6 recommended (an Arm64 size)
  9. Under Administrator Account, select SSH public key and enter your key
  10. Under Inbound Port Rules, allow SSH (port 22)
  11. Click Review + Create, then Create

Option B: Azure CLI

Run this on your own workstation, substituting your resource group, VM name and preferred region. Accept the Marketplace terms once per subscription, then create the machine:

az vm image terms accept --urn cloudimg1647283583153:oel-9-arm64-lvm:default:latest

az vm create \
  --resource-group myResourceGroup \
  --name my-oel9-arm64-lvm-vm \
  --image cloudimg1647283583153:oel-9-arm64-lvm:default:latest \
  --size Standard_D2ps_v6 \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

The --size must be an Arm64 family (Standard_D2ps_v6 above). Azure rejects an x86 size for an Arm image at create time. To start with a larger operating system disk, add --os-disk-size-gb 128 and follow Growing the root filesystem in Step 6 once the machine is up.

Step 2: Connect via SSH

Run these on your own workstation, not on the VM. Find the public IP, then connect:

az vm show --resource-group myResourceGroup --name my-oel9-arm64-lvm-vm --show-details --query publicIps -o tsv
ssh azureuser@<vm-ip>

There is no password to enter. If SSH asks you for one, the key you supplied at create time is not the key your client is offering.

Step 3: Confirm the release and the architecture

Check that the machine is Oracle Linux 9:

cat /etc/oracle-release
Oracle Linux Server release 9.8

The os-release fields carry the same information in a machine readable form, which is what configuration management tools read:

grep -E '^(NAME|VERSION|ID|VERSION_ID|PLATFORM_ID)=' /etc/os-release
NAME="Oracle Linux Server"
VERSION="9.8"
ID="ol"
VERSION_ID="9.8"
PLATFORM_ID="platform:el9"

The kernel and the page size are where the architecture shows up. aarch64 confirms this is the native 64-bit Arm build, and the el9uek suffix identifies the Unbreakable Enterprise Kernel:

uname -srm
getconf PAGESIZE
Linux 6.12.0-206.104.3.3.el9uek.aarch64 aarch64
4096

And the processor itself, which on Azure's Arm sizes is the Azure Cobalt 100, built on Arm Neoverse N2 cores:

lscpu | grep -E '^(Architecture|Vendor ID|Model name|BIOS Model name):'
Architecture:                            aarch64
Vendor ID:                               ARM
Model name:                              Neoverse-N2
BIOS Model name:                         Cobalt 100

The terminal shows oracle-release reporting Oracle Linux Server release 9.8, the os-release fields with ID ol and VERSION_ID 9.8 on platform el9, uname reporting the 6.12.0 el9uek Unbreakable Enterprise Kernel on aarch64 with a 4096 byte page size, lscpu reporting an ARM Neoverse-N2 Azure Cobalt 100 processor, and a single installed kernel-uek-core package

The version reported is the current Oracle Linux 9 point release the image was patched to. Your kernel and point release will be the same or newer.

Step 4: The kernel

On Arm, Oracle Linux 9 runs the Unbreakable Enterprise Kernel (UEK). Exactly one kernel is installed, the one the machine is running; superseded kernels are removed before capture:

rpm -q kernel-uek-core
grubby --default-kernel
kernel-uek-core-6.12.0-206.104.3.3.el9uek.aarch64
/boot/vmlinuz-6.12.0-206.104.3.3.el9uek.aarch64

New UEK releases arrive through the enabled ol9_UEKR8 repository, so a normal dnf update keeps the kernel current. On x86, Oracle Linux 9 also offers the Red Hat Compatible Kernel; Oracle's Arm repositories carry the UEK line, which is what this image boots.

Step 5: Check the patch level and automatic updates

Every available update was applied when this image was built. dnf check-update exits 100 when updates are pending and 0 when none are:

dnf -q check-update >/dev/null 2>&1; echo "exit=$?"
exit=0

Nothing is waiting on a reboot either:

dnf needs-restarting -r
No core libraries or services have been updated since boot-up.
Reboot should not be necessary.

Your update path is Oracle's rolling Oracle Linux 9 channels: BaseOS Latest, AppStream, UEK Release 8 and Addons. None of the frozen per point release repositories is enabled, so the machine always resolves the current Oracle Linux 9 packages:

dnf repolist --enabled
repo id                repo name
ol9_UEKR8              Oracle Linux 9 UEK Release 8 (aarch64)
ol9_addons             Oracle Linux 9 Addons (aarch64)
ol9_appstream          Oracle Linux 9 Application Stream Packages (aarch64)
ol9_baseos_latest      Oracle Linux 9 BaseOS Latest (aarch64)

Automatic updates

dnf-automatic is installed and its timer is enabled, so the machine applies updates on its own schedule:

systemctl is-enabled dnf-automatic.timer
systemctl cat dnf-automatic.timer | grep -E 'OnCalendar|RandomizedDelaySec'
enabled
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m

The policy applies all available updates, not only those tagged as security errata, and never reboots automatically, so a kernel update waits for a window you choose rather than restarting a production machine without warning:

grep -E '^(upgrade_type|download_updates|apply_updates|reboot) ' /etc/dnf/automatic.conf
upgrade_type = default
download_updates = yes
apply_updates = yes
reboot = never

The stock Oracle image sets upgrade_type = security. This image changes it to default, because a security only policy depends on errata metadata being attached to every update, and where it is not, that update is silently skipped. Because reboot = never, check periodically whether a restart is owed after a kernel update, using the needs-restarting -r command above. To review Oracle's security advisories yourself, run sudo dnf updateinfo list --security. To opt out of automatic updates entirely, run sudo systemctl disable --now dnf-automatic.timer.

The terminal shows dnf check-update returning exit code 0 meaning no updates are pending, needs-restarting reporting no reboot necessary, dnf repolist listing the rolling ol9_UEKR8, ol9_addons, ol9_appstream and ol9_baseos_latest repositories, dnf-automatic.timer reported as enabled with its six o clock schedule, and the automatic.conf policy showing upgrade_type default, download_updates yes, apply_updates yes and reboot never

Step 6: The LVM disk layout

This is what sets this image apart: the root filesystem sits on an LVM logical volume inside a volume group, not on a fixed partition. The /boot and EFI partitions stay as plain partitions, which is what the firmware and boot loader need:

df -hT / /var/crash /boot /boot/efi
Filesystem                 Type  Size  Used Avail Use% Mounted on
/dev/mapper/rootvg-rootlv  xfs    32G  3.7G   29G  12% /
/dev/mapper/rootvg-crashlv xfs    15G  139M   15G   1% /var/crash
/dev/sda2                  xfs   2.0G  628M  1.4G  32% /boot
/dev/sda1                  vfat  100M  7.9M   92M   8% /boot/efi

One physical volume, the third partition of the operating system disk, backs the volume group rootvg:

sudo pvs
sudo vgs
  PV         VG     Fmt  Attr PSize   PFree
  /dev/sda3  rootvg lvm2 a--  <46.90g    0
  VG     #PV #LV #SN Attr   VSize   VFree
  rootvg   1   2   0 wz--n- <46.90g    0

The volume group holds two logical volumes: rootlv for the root filesystem and crashlv, mounted at /var/crash, where kdump writes a kernel crash dump if the machine ever panics:

sudo lvs -o lv_name,lv_size,lv_path,devices rootvg
  LV      LSize   Path                Devices
  crashlv  15.00g /dev/rootvg/crashlv /dev/sda3(0)
  rootlv  <31.90g /dev/rootvg/rootlv  /dev/sda3(3840)

The kernel command line tells the early boot environment which volume group and logical volume hold the root filesystem, and lvm2 is installed so every LVM tool is available:

grep -o 'rd.lvm[^ ]*' /proc/cmdline
rpm -q lvm2 cloud-utils-growpart xfsprogs
rd.lvm.vg=rootvg
rd.lvm.lv=rootvg/rootlv
lvm2-2.03.33-4.el9_8.1.aarch64
cloud-utils-growpart-0.33-1.el9.aarch64
xfsprogs-6.18.0-1.0.4.el9.aarch64

The terminal shows df reporting the root filesystem on the rootvg-rootlv logical volume and /var/crash on rootvg-crashlv, both XFS, with /boot and /boot/efi on plain partitions, pvs and vgs showing a single physical volume /dev/sda3 backing the rootvg volume group, lvs listing rootlv and crashlv, and the rd.lvm kernel arguments naming rootvg/rootlv as the root volume

Growing the root filesystem

The volume group ships with no free space, and enlarging the Azure disk does not grow the root volume on its own: cloud-init's automatic partition growth deliberately skips a root filesystem on a logical volume, so the extra capacity stays unallocated until you claim it. After you create the machine with a larger disk (--os-disk-size-gb), or resize the disk in Azure (stop and deallocate the VM, raise the size, start it again), run these three commands. All of them work online, with no reboot:

sudo growpart /dev/sda 3
sudo pvresize /dev/sda3
sudo lvextend -r -l +100%FREE rootvg/rootlv
df -h /

growpart extends partition 3 to the end of the disk, pvresize makes LVM see the larger physical volume, and lvextend -r hands all the free space to rootlv and grows its XFS filesystem in the same step. On a machine created with a 64 GB disk, this takes / from 32 GB to 47 GB. If growpart prints NOCHANGE, the partition already fills the disk. Confirm the physical volume name with sudo pvs first; it is /dev/sda3 on this image.

Adding a data volume

Attach a new managed data disk in Azure. The portal and CLI show its LUN; the matching device is /dev/disk/azure/scsi1/lun<N>. Then either give it its own volume group, which keeps application data separate from the operating system, or add it to rootvg:

sudo pvcreate /dev/disk/azure/scsi1/lun0
sudo vgcreate datavg /dev/disk/azure/scsi1/lun0
sudo lvcreate -n datalv -l 100%FREE datavg
sudo mkfs.xfs /dev/datavg/datalv
sudo mkdir -p /data
echo '/dev/datavg/datalv /data xfs defaults,nofail 0 0' | sudo tee -a /etc/fstab
sudo mount /data

The nofail option lets the machine still boot if the data disk is ever detached.

Step 7: Verify the security posture

Confirm SELinux is enforcing:

getenforce
Enforcing

Confirm the root account is locked. The LK in the second field means locked:

passwd -S root
root LK 2009-12-22 -1 -1 -1 -1 (Alternate authentication scheme in use.)

Confirm no account on the machine has a usable password. This prints nothing on a clean image:

sudo awk -F: '($2 !~ /^[!*]/) && ($2 != "") {print $1}' /etc/shadow

Read the SSH daemon's effective policy rather than the config file, since the running daemon is what a client actually meets. without-password is OpenSSH's name for prohibit-password: root may never log in with a password, and has no key installed:

sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication) '
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no

That policy is pinned by a drop in file that sorts ahead of every other SSH configuration file, so a later package update to the main configuration cannot quietly loosen it:

grep -vE '^#' /etc/ssh/sshd_config.d/00-cloudimg-hardening.conf
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no

There is no swap on the operating system disk. A swap file baked into a Marketplace image is not permitted, so this prints nothing:

swapon --show

Confirm what is listening. SSH on port 22 is the only service reachable from the network. The only other socket is chronyd, bound to the loopback address for time synchronisation:

ss -tln
ss -uln
State  Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0      128          0.0.0.0:22        0.0.0.0:*
LISTEN 0      128             [::]:22           [::]:*
State  Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0      0          127.0.0.1:323       0.0.0.0:*
UNCONN 0      0              [::1]:323          [::]:*

The stock Oracle image enables rpcbind, which listens on port 111 for NFS version 3. NFS version 4 client mounts do not need it, so this image ships it disabled with nfs-utils still installed:

systemctl is-enabled rpcbind.socket rpcbind.service || true
disabled
disabled

If you need NFS version 3, enable it with sudo systemctl enable --now rpcbind.socket.

The terminal shows getenforce reporting Enforcing, passwd -S root reporting the root account as LK meaning locked, no accounts holding a usable password, the effective sshd policy reporting permitrootlogin without-password, pubkeyauthentication yes, passwordauthentication no and kbdinteractiveauthentication no, swapon showing no swap, ss listing sshd on port 22 as the only TCP listener and chronyd on the loopback address as the only UDP socket, rpcbind socket and service both disabled, and firewalld active

Step 8: Verify Azure integration

The Azure Linux Agent handles extensions and the portal's recovery features, and cloud-init provisions the machine:

systemctl is-active waagent
rpm -q WALinuxAgent cloud-init chrony
cloud-init status
active
WALinuxAgent-2.14.0.1-4.0.1.el9.noarch
cloud-init-24.4-8.0.1.el9_8.1.noarch
chrony-4.8-1.0.1.el9.aarch64
status: done

The kernel puts a console on ttyAMA0, the Arm PL011 serial port, which is what makes boot diagnostics and the portal's serial console work when you cannot reach the machine over SSH. On x86 images this is ttyS0; on Arm it is ttyAMA0:

grep -o 'console=ttyAMA0' /proc/cmdline | head -1
console=ttyAMA0

Time synchronisation runs through chronyd against the Azure host clock, exposed as the PHC0 reference clock (your offsets will differ). The clock and timezone are UTC:

chronyc -n sources | head -4
timedatectl | grep -E 'Time zone|synchronized|RTC in local TZ'
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#* PHC0                          0   3   377     7    +20us[  +28us] +/- 7114ns
^- 185.214.143.237               2   6   377     8  -1655us[-1647us] +/-   32ms
                Time zone: UTC (UTC, +0000)
System clock synchronized: yes
          RTC in local TZ: no

The stock Oracle image uses the America/New_York timezone with the hardware clock read as local time. Azure's host clock is UTC, so that setting shifts the clock by several hours early in every boot until chronyd corrects it. This image sets both to UTC. To display local time instead, run sudo timedatectl set-timezone Europe/London (or your zone); leave the hardware clock in UTC.

Azure Arm sizes use Accelerated Networking, which gives the machine a second, hardware network interface that is bonded to eth0 and carries the same address. NetworkManager leaves that interface alone, which is correct:

nmcli -f DEVICE,TYPE,STATE device
systemctl is-system-running
DEVICE     TYPE      STATE
eth0       ethernet  connected
lo         loopback  connected (externally)
enP6722s1  ethernet  unmanaged
running

The name of the accelerated interface differs per machine. The stock Oracle image ships a legacy interface renaming helper (initscripts-rename-device) that tries to rename that interface to eth0 on every reboot, which leaves it half configured, stalls boot for a minute and marks the system degraded. Nothing depends on that helper, so this image removes it; do not reinstall it.

Standard_D2ps_v6 has no local temporary disk, so there is no scratch disk at /mnt on that size, and the Azure temporary disk warning service is skipped rather than failing. Arm sizes with a d in the name, such as Standard_D2pds_v6, add a local temporary disk, and on those the service writes its data loss warning file as usual. Treat anything on a temporary disk as disposable.

Step 9: Networking and the firewall

The Azure Network Security Group is the control plane for inbound and outbound traffic. Oracle Linux also runs firewalld as a host firewall, and this image leaves it active, as Oracle ships it:

systemctl is-active firewalld
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --list-services
active
public
cockpit dhcpv6-client ssh

The cockpit entry is part of Oracle's default zone; Cockpit itself is not running and nothing listens on its port. Because firewalld is running, a port must be open in both the Azure NSG and firewalld before traffic reaches your service. For example, to serve HTTPS:

sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

To remove the unused Cockpit rule, run sudo firewall-cmd --permanent --remove-service=cockpit followed by sudo firewall-cmd --reload.

Step 10: Install software

AppStream carries the application packages. Everything in the repositories is built for Arm, so dnf resolves aarch64 packages automatically with nothing extra to configure:

dnf -q repoquery --arch aarch64 --latest-limit 1 nginx
nginx-2:1.20.1-28.0.1.el9_8.6.aarch64

Install a package and enable it in the usual way, for example sudo dnf install -y nginx followed by sudo systemctl enable --now nginx, then open the port in your Network Security Group and in firewalld.

To add another administrator, create the account with sudo adduser <name>, add it to the wheel group with sudo usermod -aG wheel <name>, then copy your public key into /home/<name>/.ssh/authorized_keys with mode 600 and the .ssh directory mode 700, owned by that user. The image ships with no human accounts at all, so the administrator you named at create time is the only login until you add one.

Common Use Cases

  • RHEL 9 compatible application hosting on cost efficient Azure Cobalt 100 Arm virtual machines
  • Workloads whose root or data volumes need to grow over time without rebuilding the machine
  • Oracle Linux standardised fleets extending onto Arm, managed by Ansible, Puppet or Salt
  • Container hosts and cloud native workloads on Arm with a flexible LVM storage layout

Troubleshooting

Cannot connect via SSH

Check the Network Security Group allows inbound TCP 22 from your address, confirm the machine is running, and make sure your client is offering the private key that matches the public key you supplied at create time. Password authentication is disabled, so a password prompt means key negotiation already failed. Connect as the administrator you named at create time, not as root.

az vm show --resource-group myResourceGroup --name my-oel9-arm64-lvm-vm --show-details --query powerState -o tsv

The VM will not create or will not boot

This is an Arm64 image. It only runs on Arm virtual machine sizes (the Dpsv6 and Dplsv6 Azure Cobalt 100 families and other p sizes). If Azure rejects the size at create time, confirm you selected an Arm size and not an x86 one such as Standard_B2s or Standard_D2s_v5. If the machine never becomes reachable, open Boot diagnostics and the Serial console in the portal, which read from ttyAMA0.

The disk is bigger but the root filesystem is not

That is expected on an LVM root. Follow Growing the root filesystem in Step 6: growpart, pvresize, then lvextend -r.

A service is unreachable even though the NSG allows it

firewalld is active on this image. Open the port in firewalld too, as shown in Step 9, and check with sudo firewall-cmd --list-all.

Azure agent not running

Check whether the agent is running and read its recent log:

systemctl is-active waagent
sudo journalctl -u waagent -n 5 --no-pager

If it reports anything other than active, start it:

sudo systemctl enable --now waagent

Package manager issues

Clear the metadata cache and rebuild it:

sudo dnf clean all
sudo dnf makecache

If a repository is unreachable, confirm the machine has outbound HTTPS and that DNS resolves yum.oracle.com.

A service is blocked and the logs mention SELinux

SELinux is enforcing, which is the secure default and should stay that way. Look at the denial rather than disabling SELinux:

sudo ausearch -m AVC -ts recent </dev/null 2>/dev/null || echo "no recent AVC denials"

Most denials on a fresh machine come from software installed into a non standard path. Relabel with sudo restorecon -Rv /path before considering any policy change.

Important Notes

Oracle Linux is free to download, use, patch and redistribute. A paid Oracle Linux Support subscription is optional and is not required to run or update this image. Oracle Linux is not covered by a single licence: the distribution is a collection of free and open source licences across its package set, predominantly GPL and LGPL, with MIT, BSD, Apache and others. This image is not Oracle Database and carries no database licensing.

Oracle and Oracle Linux are trademarks of Oracle Corporation. cloudimg is not affiliated with or endorsed by Oracle, and uses the name only to identify the distribution this image contains.

Oracle Linux 9 was released in July 2022 and follows Oracle's lifetime support policy: Premier Support until June 2032 and Extended Support until June 2035. Check Oracle's Linux lifetime support documentation for the dates that apply to your deployment.

Support

For assistance with this image, contact cloudimg support: