AlmaLinux 8 Arm64 on Azure User Guide
Overview
This guide covers the deployment and configuration of AlmaLinux 8 on 64-bit Arm (AArch64/arm64) on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace.
AlmaLinux is a community owned enterprise Linux distribution, produced by the AlmaLinux OS Foundation to be binary compatible with Red Hat Enterprise Linux. This is the native Arm build of AlmaLinux 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:
- AlmaLinux 8.10, native 64-bit Arm (arm64 / AArch64) with the 64k page size kernel, binary compatible with RHEL 8
- 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
This is AlmaLinux 8.10, the final AlmaLinux 8 point release
AlmaLinux 8 reached its final point release, 8.10, and will not receive another. That is not the same as end of life: AlmaLinux 8 follows the RHEL 8 lifecycle and remains in security maintenance until 31 May 2029, so 8.10 is the live, supported stream that continues to receive security errata.
This image is built on the rolling AlmaLinux 8 repositories, not on an archived vault snapshot. Your own dnf update therefore keeps pulling errata for 8.10 in the normal way. Because there is no 8.11 to move to, the release string stays at 8.10 for the life of the product, and only the package and kernel versions inside it advance.
Because this is the Arm build, it is a genuinely separate product from the x86 AlmaLinux 8 images: it carries aarch64 packages and an aarch64 kernel, and it only runs on Arm virtual machine sizes.
A note on the 64k page size
The RHEL 8 (and therefore AlmaLinux 8) Arm kernel is built with a 64 kilobyte page size, where the AlmaLinux 9 and 10 Arm kernels use 4 kilobytes. This is a property of the distribution, not a cloudimg choice, and there is only one AlmaLinux 8 Arm64 kernel, so there is no 4k variant to switch to.
It matters in a few places. Large page workloads, huge page tuning, and some JVM and database memory settings behave differently from a 4k system, and a handful of memory measurements are rounded up to the nearest 64k. Most software neither notices nor cares. If your workload is sensitive to page size, getconf PAGESIZE in Step 3 is the value to design against, and the AlmaLinux 9 Arm64 or AlmaLinux 10 Arm64 images are the 4k alternatives.
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. Usesudofromazureuserinstead.- 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 AlmaLinux default.
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 "AlmaLinux 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:almalinux-8-arm64:default:latest
az vm create \
--resource-group myResourceGroup \
--name my-almalinux-8-arm64-vm \
--image cloudimg1647283583153:almalinux-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-almalinux-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 AlmaLinux 8:
cat /etc/almalinux-release
AlmaLinux release 8.10 (Cerulean Leopard)
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="AlmaLinux"
VERSION="8.10 (Cerulean Leopard)"
ID="almalinux"
VERSION_ID="8.10"
PLATFORM_ID="platform:el8"
The RHEL compatibility lineage is asserted by the distribution itself:
cat /etc/redhat-release
AlmaLinux release 8.10 (Cerulean Leopard)
The kernel and the page size are where the architecture shows up. aarch64 confirms this is the native 64-bit Arm build, and 65536 confirms the RHEL 8 Arm kernel's 64k page size:
uname -srm
getconf PAGESIZE
Linux 4.18.0-553.162.1.el8_10.aarch64 aarch64
65536
And the processor itself, which on Azure's Arm sizes is the Azure Cobalt 100:
lscpu | grep -E '^(Architecture|Vendor ID|Model name|BIOS Model name):'
Architecture: aarch64
Vendor ID: ARM
BIOS Model name: Cobalt 100

8.10 is the final AlmaLinux 8 point release, so this string does not advance. dnf update continues to deliver security errata within 8.10.
Step 4: 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.
Exactly one kernel is installed, the one the machine is running. Superseded kernels are removed before capture:
rpm -q kernel-core
kernel-core-4.18.0-553.162.1.el8_10.aarch64
Your update path is intact. BaseOS, AppStream and Extras are enabled, which is the AlmaLinux 8 cloud image default:
dnf repolist --enabled
repo id repo name
appstream AlmaLinux 8 - AppStream
baseos AlmaLinux 8 - BaseOS
extras AlmaLinux 8 - Extras
AlmaLinux 8 also ships a developer repository, which this release names PowerTools (AlmaLinux 9 and 10 renamed it to CRB). It is present but disabled, as it is on the stock cloud image. Enable it when you need -devel packages that AppStream does not carry:
sudo dnf config-manager --set-enabled powertools
Automatic updates
The stock AlmaLinux 8 Arm64 cloud image does not include dnf-automatic. This image installs it and enables its timer, so the machine applies updates on its own schedule:
systemctl is-enabled dnf-automatic.timer
enabled
systemctl cat dnf-automatic.timer | grep -E 'OnCalendar|RandomizedDelaySec'
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 '^(apply_updates|upgrade_type|download_updates) ' /etc/dnf/automatic.conf
upgrade_type = default
download_updates = yes
apply_updates = yes
upgrade_type is deliberately default rather than security. A security only policy depends on errata metadata being present for every update, and where it is not, it silently applies nothing, so default is the setting that actually keeps the machine current. Because the tool never reboots on its own, check periodically whether a restart is owed after a kernel update, using the needs-restarting -r command above. To opt out entirely, run sudo systemctl disable --now dnf-automatic.timer.

Step 5: 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:
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication) '
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
AlmaLinux 8 ships OpenSSH 8.0p1, which predates the Include /etc/ssh/sshd_config.d/*.conf directive that AlmaLinux 9 and 10 use. There is therefore no drop in directory on this release, and the policy is pinned in the main configuration file itself:
grep -E '^(PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|PermitEmptyPasswords) ' /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
KbdInteractiveAuthentication no
If you edit /etc/ssh/sshd_config, keep those four lines, run sudo sshd -t to check your change parses, and only then sudo systemctl reload sshd.
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 AlmaLinux 8 Arm64 cloud image enables rpcbind, which binds TCP and UDP port 111 on every interface for NFS version 3. This image disables it, because NFS version 4 does not need it and an unadvertised open port does not belong in a Marketplace image. nfs-utils is retained, so NFS version 4 client mounts work unchanged:
systemctl is-enabled rpcbind.socket rpcbind.service || true
disabled
disabled
It is disabled, not masked, so if you need NFS version 3 you can turn it back on with sudo systemctl enable --now rpcbind.socket and open only what you need in your Network Security Group.

Step 6: 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
active
WALinuxAgent-2.7.0.6-8.el8_8.noarch
cloud-init-23.4-7.el8_10.11.alma.2.noarch
chrony-4.5-2.el8_10.aarch64
Cloud-init should report done, which means provisioning finished cleanly:
cloud-init status
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
console=ttyAMA0
Time synchronisation runs through chronyd against the Azure host clock (your offsets will differ):
chronyc -n sources | head -4
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* PHC0 2 3 377 7 +34us[ +37us] +/- 8398ns
The operating system disk uses a flat layout with XFS for the root and boot filesystems:
df -h /
findmnt -no SOURCE,FSTYPE,TARGET /boot
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 29G 3.2G 26G 11% /
/dev/sda2 xfs /boot
Standard_D2ps_v6 has no local temporary disk, so there is no scratch disk at /mnt on that size. Arm sizes with a d in the name, such as Standard_D2pds_v6, add a local temporary disk. Treat anything on a temporary disk as disposable, since it is erased whenever the machine is deallocated or moved.

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.
Unlike the AlmaLinux 9 and 10 cloud images, the AlmaLinux 8 cloud image does install firewalld, but it ships disabled and inactive, so it is not filtering anything:
rpm -q firewalld
systemctl is-enabled firewalld || true
systemctl is-active firewalld || true
firewalld-0.9.11-12.el8_10.alma.1.noarch
disabled
inactive
With firewalld inactive, 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, it is already installed, so you only need to enable it:
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload
Add the SSH rule before you reload, or you will lock yourself out of the machine. Once firewalld is running, a port must be open in both the Azure NSG and firewalld before traffic reaches your service.
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.10.0+4171+d7eec01b.alma.1 appstream
AlmaLinux 8 uses modules (AppStream module streams) for software that ships in several versions, which is why the package above carries a module_el8 tag. List the streams for a package and pick one before installing:
dnf module list nginx
sudo dnf module enable -y nginx:1.24
sudo dnf install -y nginx
Then enable the service 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
Common Use Cases
- RHEL 8 compatible application hosting on cost efficient Azure Cobalt 100 Arm virtual machines
- Migrating an existing RHEL 8 or CentOS 8 Arm estate to Azure without a subscription cost
- A RHEL compatible platform for workloads certified against RHEL 8 and its module streams
- Arm build and CI runners, 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 login over SSH is disabled too, so connect as the administrator you named at create time.
az vm show --resource-group myResourceGroup --name my-almalinux-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.
Software reports an unexpected memory or page size
This is the 64k page size Arm kernel described above. Check the value with getconf PAGESIZE and configure huge pages, JVM heap regions or database shared buffers against 65536 rather than 4096. If your workload genuinely requires a 4k page size, use the AlmaLinux 9 Arm64 or AlmaLinux 10 Arm64 image instead.
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 AlmaLinux's mirror system is reached over both.
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
AlmaLinux 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.
AlmaLinux is a trademark of the AlmaLinux OS Foundation. cloudimg is not affiliated with or endorsed by the AlmaLinux OS Foundation, and uses the name only to identify the distribution this image contains.
AlmaLinux 8 follows the RHEL 8 lifecycle. 8.10 is the final point release, and security maintenance runs until 31 May 2029. Check the AlmaLinux release notes for the dates that apply to your deployment, and plan a migration to AlmaLinux 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