Deployment guide

Rocky 10 arm64

Overview

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 image is the native 64-bit Arm (AArch64/arm64) build of Rocky Linux 10 for Microsoft Azure. It runs the 6.12 kernel and follows the RHEL 10 lifecycle.

The image runs on Azure's Arm virtual machine sizes, which are powered by the Azure Cobalt 100 processor (the Dpsv6 and Dplsv6 families). Many workloads deliver the same performance on these sizes at a lower cost than on x86.

What you get:

  • Rocky Linux 10, native 64-bit Arm (arm64 / AArch64) with the standard 4k page size kernel, binary compatible with RHEL 10
  • 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 SecureAnyCloud support

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

This is the Rocky Linux 10 major release, on Arm

This listing tracks the Rocky Linux 10 major release, not a frozen minor. RHEL-family security fixes roll into the latest minor stream, so the image is built from whichever Rocky Linux 10 point release is current at build time and brought fully up to date. Your own dnf update then rolls the machine forward within Rocky Linux 10 in the normal way, so you keep receiving security fixes.

Rocky Linux 10 reached general availability on 11 June 2025. It has active support until 31 May 2030 and security support until 31 May 2035.

The Arm build is a separate product from the x86 Rocky Linux 10 images. It carries aarch64 packages and an aarch64 kernel, and it runs only on Arm virtual machine sizes. It uses the flat partition layout, not the LVM variant, and the standard 4k page size kernel.

What this image changes from the stock Rocky Linux 10 Arm image

Each change below was measured on the stock vendor image before it was made, and each one is reversible with a single command. Nothing is removed. The packages stay installed so you can turn any of it back on.

  • The Cockpit web console is switched off. The stock image enables cockpit.socket, which listens on *:9090 on every interface. An operating system base image should expose one service, and that service is SSH, so cockpit.socket ships disabled. To re-enable it, run sudo systemctl enable --now cockpit.socket and open 9090 in your network security group.
  • rngd is switched off. The rng-tools entropy daemon is enabled by default but has no usable entropy source on Azure Cobalt 100. There is no backing hardware random number generator, and the Neoverse N2 core does not implement the Armv8.5 RNDR instruction. As a result, rngd fails at every boot and leaves the machine reporting degraded. The Linux 6.12 kernel seeds its random pool without it, so the unit ships disabled. If you attach an entropy source, re-enable it with sudo systemctl enable --now rngd.service.
  • Automatic updates are installed and armed. The stock image has no dnf-automatic at all.
  • SSH is hardened by a drop-in file that sorts ahead of every other SSH configuration file, so a later package update cannot quietly loosen it.

The Cockpit and rngd changes are recorded on the machine itself, at /var/lib/sac/data/cockpit-disabled and /var/lib/sac/data/rngd-disabled, together with the command that restores each one.

Security posture

This image ships with no known credential. It has no default password and no built-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.
  • PermitRootLogin is set to prohibit-password, so root can never log in with a password. No root key ships in the image, so in practice root has no SSH login at all until you add one deliberately. 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 is 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.

Prerequisites

Before you deploy this image, make sure 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 you must launch it on an Arm virtual machine size, such as the Dpsv6 or Dplsv6 families. An x86 size such as Standard_B2s does not accept this image.

1

Deploy the virtual machine

Option A: Azure Portal

  • Open the Azure Marketplace and search for "Rocky Linux 10 Arm64 SecureAnyCloud".
  • Select the image and click Create.
  • On the basics tab, set:
    • Subscription: select your Azure subscription
    • Resource Group: create a new one or select an existing one
    • Virtual Machine Name: enter a name for your VM
    • Region: select a region that offers Arm sizes
    • Size: Standard_D2ps_v6 is recommended (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 the following 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 sac1647283583153:rocky-10-arm64:default:latest
az vm create \
  --resource-group myResourceGroup \
  --name my-rocky-10-arm64-vm \
  --image sac1647283583153:rocky-10-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 when you create the VM.

2

Connect via SSH

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

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

There is no password to enter. If SSH asks for one, your client is not offering the key you supplied when you created the VM.

3

Confirm the release and the architecture

Check that the machine runs Rocky Linux 10:

cat /etc/rocky-release
Rocky Linux release 10.2 (Red Quartz)

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="10.2 (Red Quartz)" ID="rocky" VERSION_ID="10.2" PLATFORM_ID="platform:el10"

The RHEL compatibility lineage is asserted by the distribution itself:

cat /etc/redhat-release
Rocky Linux release 10.2 (Red Quartz)

Check the kernel and page size to confirm the architecture. aarch64 confirms the native 64-bit Arm build, and 4096 confirms the standard 4k page size kernel:

uname -srm getconf PAGESIZE
Linux 6.12.0-211.54.1.el10_2.aarch64 aarch64 4096

Then check the processor. On Azure's Arm sizes it 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 None CPU @ 3.4GHz

The version reported is the Rocky Linux 10 point release the image was patched to. This listing tracks the 10 major release, so dnf update rolls the machine forward across future point releases.

4

Check the patch level and automatic updates

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

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

Confirm that no reboot is pending:

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, and it is the one the machine runs. The superseded kernel that the vendor base carried is removed before capture, so the image is not flagged for an advisory against a kernel it does not run:

rpm -q kernel-core
kernel-core-6.12.0-211.54.1.el10_2.aarch64

Check that your update path is intact. BaseOS, AppStream and Extras are enabled, which is the Rocky Linux 10 cloud image default:

dnf repolist --enabled
repo id repo name appstream Rocky Linux 10 - AppStream baseos Rocky Linux 10 - BaseOS extras Rocky Linux 10 - Extras

CRB (CodeReady Builder) and the Rocky security repository are present but disabled, matching the vendor default. When you need build-time dependencies, enable CRB with sudo dnf config-manager --set-enabled crb.

Automatic updates

The stock Rocky Linux 10 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. Confirm the timer and its 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. It never reboots automatically, so a kernel update waits for a window you choose instead of restarting a production machine without warning:

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

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 missing, the policy silently applies nothing. default is the setting that keeps the machine current.

Because reboot = never, check periodically whether a restart is owed after a kernel update, using the needs-restarting -r command above. To opt out of automatic updates entirely, run sudo systemctl disable --now dnf-automatic.timer.

5

Verify the security posture

Confirm SELinux is enforcing:

getenforce
Enforcing

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

passwd -S root
root L 2009-12-22 -1 -1 -1 -1

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, because the running daemon is what a client actually meets. OpenSSH reports prohibit-password as without-password:

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

A drop-in file pins that policy. It 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-sac-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 belongs to 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 [::]:*

This image closes two listeners that the stock vendor image leaves open. The Cockpit web console would otherwise bind *:9090 on every interface, and rpcbind, which NFS version 3 relies on, would bind port 111. Both units ship disabled rather than removed:

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

The base operating system still ships nfs-utils for NFS version 4 client mounts, which need no extra listener. If you need NFS version 3, run sudo systemctl enable --now rpcbind.socket and open only what you need in your network security group.

To bring back the Cockpit console, run sudo systemctl enable --now cockpit.socket, then open 9090 in the network security group.

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.14.0.1-3.el10.noarch cloud-init-24.4-7.el10_2.1.rocky.0.1.noarch chrony-4.8-2.el10.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. This makes boot diagnostics and the portal's serial console work when you cannot reach the machine over SSH. On x86 images the console is ttyS0, and on Arm it is ttyAMA0:

grep -o 'console=ttyAMA0' /proc/cmdline
console=ttyAMA0

Confirm that nothing failed on boot. On the stock vendor image, rngd fails here and the machine reports degraded. With that unit disabled, the system reaches running with no failed units:

systemctl is-system-running
running

Time synchronisation runs through chronyd against the Azure host clock (your offsets will differ):

chronyc -n sources | head -3
MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #* PHC0 2 3 377 7 +28us[ +29us] +/- 9523ns

The operating system disk uses a flat layout with XFS for the root and boot filesystems, and no LVM:

lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT df -h /
NAME SIZE TYPE FSTYPE MOUNTPOINT sda 10G disk ├─sda1 100M part vfat /boot/efi ├─sda2 1000M part xfs /boot └─sda3 8.9G part xfs / /dev/sda3 8.9G 1.3G 7.6G 15% /

The root partition grows to fill the operating system disk you choose when you create the VM. If you ask for a larger disk than the image's 10 GB, you get the space without a manual resize.

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, because it is erased whenever the machine is deallocated or moved.

7

Networking and the firewall

The Azure network security group (NSG) controls inbound and outbound traffic. 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)

This 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 also requires a host firewall, 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.

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 2:1.26.3-6.el10_2.6 appstream

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.

To add another administrator, create the account with sudo adduser <name> and add it to the wheel group with sudo usermod -aG wheel <name>. Then copy your public key into /home/<name>/.ssh/authorized_keys, owned by that user, with mode 600 on the file and mode 700 on the .ssh directory.

The image ships with no human accounts at all. The administrator you named when you created the VM is the only login until you add another:

getent group wheel
wheel:x:10:

Common use cases

  • RHEL 10-compatible application hosting on cost-efficient Azure Cobalt 100 Arm virtual machines
  • Container platforms and cloud-native workloads on Arm
  • A RHEL-compatible platform for workloads certified against RHEL 10, without a subscription
  • Arm build and CI runners, and fleets managed by Ansible, Puppet or Salt

Troubleshooting

Cannot connect via SSH

Check that the network security group allows inbound TCP 22 from your address and that the machine is running. Make sure your client offers the private key that matches the public key you supplied when you created the VM.

Password authentication is disabled, so a password prompt means key negotiation already failed. Root cannot log in with a password and no root key ships in the image, so connect as the administrator you named when you created the VM.

az vm show --resource-group myResourceGroup --name my-rocky-10-arm64-vm --show-details --query powerState -o tsv

The VM will not create or will not boot

This is an Arm64 image. It runs only on Arm virtual machine sizes (the Dpsv6 and Dplsv6 Azure Cobalt 100 families and other p sizes). If Azure rejects the size when you create the VM, confirm that 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 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 it with sudo restorecon -Rv /path before you consider any policy change.

The boot log mentions an unmaintained nft_compat driver

Two kernel messages appear at error level on every boot of Rocky Linux 10 on Arm:

journalctl -p err -b --no-pager | grep -c nft_compat
2

They read Warning: Unmaintained driver is detected: nft_compat. They come from the in-kernel compatibility layer that lets the old iptables command line drive nftables, which upstream has marked unmaintained.

The messages are informational and come from a stock Rocky Linux 10 kernel module. Nothing in this image introduces them, and they are safe to ignore.

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. SecureAnyCloud 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 10 follows the RHEL 10 lifecycle. It reached general availability on 11 June 2025, active support runs until 31 May 2030, and security support runs until 31 May 2035. Check the Rocky Linux release notes for the dates that apply to your deployment.

Support

For assistance with this image, contact SecureAnyCloud support:

  • Website: secureanycloud.com
  • Product catalogue: secureanycloud.com/products
  • User guides: secureanycloud.com/guides
  • SLA: 24-hour response guaranteed

Deploy on Azure

Launch Rocky Linux 10 Arm64 on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure