Rocky Linux 8 Arm64 on Azure User Guide
Overview
This guide covers the deployment and configuration of Rocky Linux 8 on 64-bit Arm (AArch64/arm64) on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace.
Rocky Linux is a community enterprise operating system, produced by the Rocky Enterprise Software Foundation to be binary compatible with Red Hat Enterprise Linux. This is the native Arm build of Rocky Linux 8, running the 4.18 kernel and following the RHEL 8 lifecycle. 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.
What's included:
- Rocky Linux 8.10, native 64-bit Arm (arm64 / AArch64), binary compatible with RHEL 8
- A 64k page size kernel, which is what RHEL 8 ships on aarch64 (see the section below, because it is visible to your tuning)
- Every available update applied at build time, and again immediately before capture
- Automatic updates installed, armed and enabled, so the machine keeps patching itself
- Azure Linux Agent (waagent) and cloud-init for Azure integration
- Chronyd for NTP time synchronisation against the Azure host clock
- SELinux in enforcing mode
- BaseOS, AppStream and Extras repositories enabled, so your update path is intact
- 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
Rocky Linux 8.10 is the final Rocky Linux 8 release, and it is still supported
Rocky Linux 8 ends at 8.10. There will be no 8.11, so unlike the Rocky Linux 9 and 10 listings — which track a moving minor — this image is pinned to the release the 8 series terminates on.
That is not the same thing as a frozen back catalogue image. Rocky Linux 8 is in security maintenance until 31 May 2029, and 8.10 is the live stream that receives those errata. This image therefore stays on the rolling Rocky Linux 8 repositories — BaseOS, AppStream and Extras, with no vault.rocky.org pin — so your own dnf update keeps pulling security fixes for the life of the release. You get a supported platform, not an archived one.
Because this is the Arm build, it is a genuinely separate product from the x86 Rocky Linux 8 images: it carries aarch64 packages and an aarch64 kernel, and it only runs on Arm virtual machine sizes. It uses the flat partition layout, not the LVM variant.
The page size is 64k, and that is worth knowing before you tune
RHEL 8 builds its aarch64 kernel with a 64 KiB page size. RHEL 9 moved the aarch64 kernel to 4 KiB, so if you are moving a workload here from a Rocky Linux 9 or 10 Arm machine, this is the one platform difference most likely to surprise you.
getconf PAGESIZE
65536
The most visible consequence is huge pages. On a 4k kernel the default huge page is 2 MiB; on this kernel it is 512 MiB:
grep -E '^Hugepagesize' /proc/meminfo; ls /sys/kernel/mm/hugepages/
Hugepagesize: 524288 kB
hugepages-16777216kB
hugepages-2048kB
hugepages-524288kB
Practical implications:
- Databases that reserve huge pages (PostgreSQL
huge_pages, MySQLlarge-pages, Oracle) allocate in 512 MiB units here, so round your shared buffer or SGA sizing to that granularity rather than copying a 2 MiB-based figure from an x86 or EL9 host. - JVM workloads using
-XX:+UseLargePagesinherit the same 512 MiB granularity.-XX:LargePageSizeInBytesvalues carried over from a 4k host will not apply cleanly. - Memory accounting looks coarser. Small allocations round up to 64 KiB, so a process with many small mappings reports a larger resident set than the same process on a 4k kernel. This is expected and is not a leak.
- Anything that assumes
PAGE_SIZE == 4096at compile time needs rebuilding. Everything in the Rocky Linux repositories is already built correctly for this kernel.
What this image changes from the stock Rocky Linux 8 Arm image
Everything below was measured on the stock vendor image before it was changed, and every change is reversible in one command. Nothing is removed: the packages stay installed so you can turn any of it back on.
rpcbindis switched off. This is the significant one. The stock image enables bothrpcbind.socketandrpcbind.service, which between them bind four sockets on port 111 (IPv4 and IPv6, TCP and UDP) on every interface. That is an unadvertised network listener on every machine you launch. An operating system base image should expose one service, and that service is SSH, so both units ship disabled. NFSv4 does not needrpcbind; if you need NFSv3 or another RPC service, re-enable it withsudo systemctl enable --now rpcbind.socket.bluetooth.serviceis switched off. Bluetooth is not on the stock image at all —bluezarrives as a dependency of the full security patch, and its unit is enabled by RPM preset. An Azure virtual machine has no Bluetooth radio, so the unit is pointless and ships disabled. Re-enable it withsudo systemctl enable --now bluetooth.service.- Automatic updates are installed and armed. The stock image has no
dnf-automaticat all — no package, no configuration, no timer. It is installed here and armed withupgrade_type = defaultandapply_updates = yes, so the machine keeps patching itself. - SSH is hardened in the main configuration file. The stock image permits root login and X11 forwarding. Note that Rocky Linux 8 ships OpenSSH 8.0p1, which predates the
Include /etc/ssh/sshd_config.d/*.confdirective — there is no such directory and no such include — so the hardening is applied to/etc/ssh/sshd_configitself, which is the only filesshdactually reads on this release. A drop in file would be silently ignored here. - A pointless network interface rename is suppressed. The stock image logs
Error changing net interface name 'eth1' to 'eth0': File existsat error level on every boot after the first, because Azure's Accelerated Networking virtual function shares the primary interface's MAC address. An empty/etc/udev/rules.d/60-net.rulesoverride stops the attempt. Nothing about your networking changes: the kernel already names the interfaceseth0andeth1becausenet.ifnames=0is on the command line.
Two things you might expect from the sibling Rocky Linux 10 Arm64 listing are not changed here, because they were measured on this base and did not need changing:
- Cockpit is already off.
cockpit-wsis installed butcockpit.socketis disabled and nothing is listening on 9090. On the Rocky Linux 10 Arm image the socket is enabled; on this one it is not. Enable it withsudo systemctl enable --now cockpit.socketand open 9090 in your Network Security Group if you want the web console. rngdis left running. On the Rocky Linux 10 Arm image the entropy daemon has no usable source and leaves the machinedegraded. On Rocky Linux 8 the vendor's/etc/sysconfig/rngdleaves the jitter entropy source available, sorngdstarts cleanly and the machine reportsrunningwith no failed units. It is left exactly as the distribution ships it.
The two changes that close a listener are recorded on the machine itself, at /var/lib/cloudimg/rpcbind-disabled and /var/lib/cloudimg/bluetooth-disabled, together with the command that restores each one.
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.
PasswordAuthenticationand keyboard interactive authentication are disabled in the SSH daemon, so every login is by key.PermitRootLoginis set tono, so root cannot log in over SSH at all. Usesudofrom the administrator account you named at create time.- 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-idis 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, the Rocky Linux default.
- SSH on port 22 is the only off-box listener. Chrony's port 323 is bound to the loopback address only.
Prerequisites
Before deploying this image, ensure you have:
- An active Microsoft Azure subscription
- Access to the Azure Portal or Azure CLI
- An SSH key pair for Linux VM access
- 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
- Navigate to the Azure Marketplace and search for "Rocky Linux 8 Arm64 cloudimg"
- Select the image and click Create
- Configure the basics:
- Subscription: Select your Azure subscription
- Resource Group: Create new or select existing
- Virtual Machine Name: Enter a name for your VM
- Region: Select a region that offers Arm sizes
- Size:
Standard_D2ps_v6recommended (an Arm64 size) - Under Administrator Account, select SSH public key and enter your key
- Under Inbound Port Rules, allow SSH (port 22)
- 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:rocky-8-arm64:default:latest
az vm create \
--resource-group myResourceGroup \
--name my-rocky-8-arm64-vm \
--image cloudimg1647283583153:rocky-8-arm64: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.
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-rocky-8-arm64-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 Rocky Linux 8:
cat /etc/rocky-release
Rocky Linux release 8.10 (Green Obsidian)
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="Rocky Linux"
VERSION="8.10 (Green Obsidian)"
ID="rocky"
VERSION_ID="8.10"
PLATFORM_ID="platform:el8"
The RHEL compatibility lineage is asserted by the distribution itself:
cat /etc/redhat-release
Rocky Linux release 8.10 (Green Obsidian)
Confirm the kernel and the architecture. Both must report aarch64:
uname -srm
Linux 4.18.0-553.162.1.el8_10.aarch64 aarch64
And the processor this is running on. BIOS Model name is where Azure reports the host CPU, which on an Arm size is the Azure Cobalt 100:
lscpu | grep -E '^(Architecture|Vendor ID|Model name|BIOS Model name|CPU\(s\)):'
Architecture: aarch64
CPU(s): 2
Vendor ID: ARM
BIOS Model name: Cobalt 100

Step 4: Check the patch level and automatic updates
The image is fully patched at capture. dnf check-update exits 0 when nothing is pending and 100 when updates are waiting, so 0 is the answer you want:
sudo dnf -q check-update >/dev/null 2>&1; echo "exit=$?"
exit=0
Nothing is waiting on a reboot either:
sudo needs-restarting -r
No core libraries or services have been updated since boot-up.
Reboot should not be necessary.
Your update path is intact — the three repositories a Rocky Linux 8 machine needs are enabled, and none of them is pinned to the vault archive:
sudo dnf repolist --enabled
repo id repo name
appstream Rocky Linux 8 - AppStream
baseos Rocky Linux 8 - BaseOS
extras Rocky Linux 8 - Extras
The PowerTools repository (RHEL 8's developer package set, renamed crb in RHEL 9 and later) is present but disabled, as it is on a stock Rocky Linux 8 machine. Enable it with sudo dnf config-manager --set-enabled powertools if a build needs it.
Automatic updates
The stock Rocky Linux 8 Arm image has no dnf-automatic at all. This image installs it and arms it, so the machine keeps patching itself without you scheduling anything:
systemctl is-enabled dnf-automatic.timer
enabled
systemctl cat dnf-automatic.timer | grep -E 'OnCalendar|RandomizedDelaySec'
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m
The policy applies updates rather than only downloading them:
grep -E '^(apply_updates|upgrade_type|download_updates)' /etc/dnf/automatic.conf
upgrade_type = default
download_updates = yes
apply_updates = yes
upgrade_type is deliberately default and not security. Rocky Linux does not publish the updateinfo security metadata that a security policy filters on, so a security setting would quietly apply nothing at all, forever. default applies the errata that are actually published.
Exactly one kernel ships in the image, so there is no superseded kernel for a vulnerability scanner to report:
rpm -q kernel
kernel-4.18.0-553.162.1.el8_10.aarch64

Step 5: Verify the security posture
SELinux is enforcing, and persists that way across reboots:
getenforce; grep -E '^SELINUX=' /etc/selinux/config
Enforcing
SELINUX=enforcing
The root account is locked. LK is the locked state:
sudo passwd -S root
root LK 2009-12-22 -1 -1 -1 -1 (Alternate authentication scheme in use.)
No account on the machine carries a usable password hash at all, so there is nothing to guess. This command prints nothing on a correct image:
sudo awk -F: '($2 !~ /^[!*]/) && ($2 != "") {print $1}' /etc/shadow
The effective SSH policy is what sshd actually computes, which is the only thing worth checking. Root login is refused outright, and password and keyboard interactive authentication are both off:
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication|x11forwarding|gssapiauthentication|maxauthtries) '
maxauthtries 4
permitrootlogin no
pubkeyauthentication yes
gssapiauthentication no
passwordauthentication no
kbdinteractiveauthentication no
x11forwarding no
There is no swap in the image, which is what Azure expects — swap belongs on the ephemeral resource disk, managed by waagent, never on the captured operating system disk. This command prints nothing:
swapon --show
SSH is the only thing listening off-box. TCP 22 is the whole set:
ss -tln
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
And the only UDP sockets are chrony's, bound to the loopback address, so they are not reachable from the network:
ss -uln
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
UNCONN 0 0 127.0.0.1:323 0.0.0.0:*
UNCONN 0 0 [::1]:323 [::]:*
The three units that would otherwise have opened a port, or run a radio stack that does not exist on a virtual machine, are all disabled — and disabled, not masked, so any of them is one command from working:
for u in rpcbind.socket bluetooth.service cockpit.socket; do printf '%s: %s\n' "$u" "$(systemctl is-enabled $u 2>&1 | head -1)"; done
rpcbind.socket: disabled
bluetooth.service: disabled
cockpit.socket: disabled

Step 6: Verify Azure integration
The Azure Linux Agent is running and starts at boot:
systemctl is-active waagent; systemctl is-enabled waagent
active
enabled
The guest integration packages are all the aarch64 or noarch builds, as they should be on an Arm machine:
rpm -q WALinuxAgent cloud-init chrony
WALinuxAgent-2.7.0.6-8.el8_8.rocky.0.1.noarch
cloud-init-23.4-7.el8_10.11.0.2.noarch
chrony-4.5-2.el8_10.aarch64
cloud-init finished cleanly on this boot:
cloud-init status
status: done
The serial console is on ttyAMA0, the Arm PL011 UART, which is what Azure's Boot diagnostics and Serial console read from. On x86 this would be ttyS0:
grep -o 'console=ttyAMA0[^ ]*' /proc/cmdline
console=ttyAMA0
Nothing failed on this boot:
systemctl is-system-running; systemctl list-units --state=failed --no-legend --plain
running
Networking comes up promptly. Azure's Accelerated Networking virtual function is left unmanaged by NetworkManager, so NetworkManager-wait-online does not wait on it:
systemd-analyze blame | grep NetworkManager-wait-online
916ms NetworkManager-wait-online.service
The clock is synchronised against the Azure host's precision clock rather than a public NTP pool, which is the correct source on a virtual machine:
systemctl is-active chronyd; chronyc -n sources | head -3
active
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* PHC0 2 3 377 4 +29us[ +37us] +/- 8564ns
The disk layout is flat — partitions directly on the disk, with no LVM in the way, so growing the root filesystem is a single growpart plus xfs_growfs with nothing else to resize:
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT
NAME SIZE TYPE FSTYPE MOUNTPOINT
sda 11G disk
├─sda1 99M part vfat /boot/efi
├─sda2 1000M part xfs /boot
├─sda3 4M part
├─sda4 1M part
└─sda5 9.9G part xfs /
sda3 and sda4 are small unformatted PReP and BIOS boot partitions that the vendor image carries for boot compatibility; they hold no filesystem and need no attention.
The operating system disk is 11 GiB. If you need more room, resize the disk in Azure and grow the root partition and filesystem:
sudo growpart /dev/sda 5
sudo xfs_growfs /

Step 7: Networking and the firewall
The Azure Network Security Group is the control plane for inbound and outbound traffic, and it is the first and usually the only place you need to open a port.
firewalld is not installed on this image:
rpm -q firewalld || echo "(firewalld absent - the Azure NSG is your only firewall)"
package firewalld is not installed
(firewalld absent - the Azure NSG is your only firewall)
That matches the Rocky Linux cloud image default: with no host firewall in the way, your NSG rules take effect exactly as written, with no second layer to keep in sync. If your policy requires a host firewall as well, install and enable it yourself:
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload
Once firewalld is running, a port must be open in both the Azure NSG and firewalld before traffic reaches your service.
Your machine has two network interfaces, and that is normal. eth1 is the Accelerated Networking virtual function; it shares eth0's MAC address and is deliberately left unmanaged, so you configure eth0 and ignore eth1:
nmcli -t -f DEVICE,STATE,TYPE device
eth0:connected:ethernet
eth1:unmanaged:ethernet
lo:unmanaged:loopback
Step 8: 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 list --available nginx 2>/dev/null | tail -2
Available Packages
nginx.aarch64 1:1.14.1-9.module+el8.4.0+542+81547229 appstream
Rocky Linux 8 uses modules (AppStream module streams) for several major packages, which is a RHEL 8 concept that RHEL 9 and 10 dropped. If you want a newer stream than the default, list what is available and enable one before installing:
sudo dnf module list nginx
sudo dnf module enable -y nginx:1.22
sudo dnf install -y nginx
Then enable the service in the usual way with sudo systemctl enable --now nginx, and open the port in your Network Security Group.
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:
getent group wheel
wheel:x:10:
Common Use Cases
- RHEL 8 compatible application hosting on cost efficient Azure Cobalt 100 Arm virtual machines
- Lifting an existing RHEL 8 or CentOS 8 workload onto Arm without changing the major release
- A RHEL compatible platform for workloads certified against RHEL 8 without a subscription
- Arm build and CI runners producing
el8packages, and fleets managed by Ansible, Puppet or Salt
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. Root cannot log in over SSH at all, so connect as the administrator you named at create time.
az vm show --resource-group myResourceGroup --name my-rocky-8-arm64-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.
Azure agent not running
Check whether the agent is running and read its recent log:
systemctl is-active waagent
sudo journalctl -u waagent -n 20 --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, since Rocky Linux's mirror system is reached over both.
A package needs a newer version than the one in AppStream
Rocky Linux 8 uses AppStream module streams. The default stream is often not the newest one packaged. Check what streams exist before adding a third party repository:
sudo dnf module list <package>
Enabling a different stream with sudo dnf module enable -y <package>:<stream> is usually the right answer, and keeps you on distribution packages that receive Rocky Linux errata.
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.
The boot log mentions a dhclient timeout being reduced
One message appears at error level on every boot:
journalctl -p err -b --no-pager | grep -c 'Timeout too large'
1
It reads dhclient: Timeout too large reducing to: 2147483646 (TIME_MAX - 1). Azure's DHCP server offers an effectively infinite lease time, and the stock Rocky Linux 8 dhclient clamps it to the largest value it can represent, logging as it does so. Your lease is fine and your networking is unaffected. It is emitted by a stock distribution component, nothing in this image introduces it, and it is safe to ignore.
A memory figure looks larger than expected
This kernel uses 64 KiB pages, so every allocation rounds up to 64 KiB rather than 4 KiB. A process with many small mappings therefore reports a larger resident set here than the identical process on a Rocky Linux 9 or 10 Arm machine. This is an accounting difference, not a leak. See the page size section above for the tuning settings it affects.
Important Notes
Rocky Linux is free and open source. It 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. No subscription, entitlement or licence key is required, and no per seat cost applies.
Rocky Linux is a trademark of the Rocky Enterprise Software Foundation. cloudimg is not affiliated with or endorsed by the Rocky Enterprise Software Foundation, and uses the name only to identify the distribution this image contains.
Rocky Linux 8 follows the RHEL 8 lifecycle. 8.10 is the final release of the 8 series, and Rocky Linux 8 is in security maintenance until 31 May 2029. Check the Rocky Linux release notes for the dates that apply to your deployment, and plan a move to Rocky Linux 9 or 10 before that date.
Support
For assistance with this image, contact cloudimg support:
- Website: www.cloudimg.co.uk
- Product Catalogue: www.cloudimg.co.uk/products
- User Guides: www.cloudimg.co.uk/guides
- SLA: 24 hour response guaranteed