Rocky Linux 8 on Azure User Guide
Overview
This guide covers the deployment and configuration of Rocky Linux 8 (Rocky Linux release 8.10, Green Obsidian) on Microsoft Azure, using cloudimg's Rocky Linux 8 virtual machine image from the Azure Marketplace.
Rocky Linux is a community enterprise Linux distribution from the Rocky Enterprise Software Foundation, built to be binary compatible with Red Hat Enterprise Linux 8. Version 8.10 is the final minor release of the 8 series: updates keep you on 8.10 while security fixes continue to arrive.
Every command and every output in this guide was run on virtual machines deployed from the public Marketplace image (image version 4.0.1), as the azureuser account a customer gets.
Support lifecycle
Rocky Linux 8 is in maintenance support until 31 May 2029 (Rocky Linux downloads, planned end of life). Until then the Rocky Linux project publishes security and bug fixes for the 8.10 stream. Plan a move to a Rocky Linux 9 or 10 image before that date for workloads that need to outlive it.
This image was patched when it was built. Updates published after that build are not applied automatically, either at boot or on a timer, so a fresh virtual machine has updates waiting. Step 4 shows how to check for and apply them.
What's included
- Rocky Linux 8.10 (Green Obsidian), binary compatible with RHEL 8
- Azure Linux Agent (
waagent, WALinuxAgent 2.7.0.6) active and enabled, plus cloud-init 23.4, for Azure provisioning - Chronyd for time synchronisation, using the Azure host clock
- SELinux in enforcing mode
- The BaseOS, AppStream and Extras repositories enabled, so your update path is intact
- Python 3.6.8 (
python3, the platform Python thatdnfuses) - Two plans: one for Gen1 virtual machines (BIOS boot) and one for Gen2 virtual machines (UEFI boot)
- 24/7 cloudimg support
Platform: Microsoft Azure (Gen1 or Gen2 Hyper V, by plan)
Default user: azureuser (or the administrator name you choose at create time)
The Azure CLI is not installed on the image. Step 6 shows how to add it if you want it on the virtual machine.
Security posture
This image ships with no default password, and SSH accepts keys only:
PasswordAuthenticationandKbdInteractiveAuthenticationarenoin the SSH daemon, so every login is by key.- The key that logs you in is the public key you supply when you create the virtual machine. Azure installs it for your administrator user at first boot.
- The SSH host keys and
/etc/machine-idare generated on each virtual machine at first boot, so no two machines you launch share them. - The root password is locked. SSH logins as
rootonly printPlease login as the user "azureuser" rather than the user "root".and close the connection. - SELinux is in enforcing mode, the Rocky Linux default.
- No host firewall is installed; the Azure Network Security Group controls inbound traffic (Step 7).
Prerequisites
Before deploying this image, ensure you have:
- An active Microsoft Azure subscription
- Access to the Azure Portal or the Azure CLI
- An SSH key pair for Linux virtual machine access
- Familiarity with Azure virtual machine management
Choose a virtual machine size that suits your workload, and the plan that matches it: the first plan for Gen1 sizes, the Gen2 plan for Gen2 sizes.
Step 1: Deploy the Virtual Machine
Option A: Azure Portal
- Navigate to the Azure Marketplace and search for "Rocky Linux 8 cloudimg"
- Open the Rocky Linux 8 listing and choose Get it now, then 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 your preferred Azure region
- Size: Select a size for your workload
- Under Administrator Account, select SSH public key and enter your key
- Under Inbound Port Rules, allow SSH (port 22), ideally from your own network only
- Click Review + Create, then Create
Option B: Azure CLI
The image carries a Marketplace purchase plan, so accept its terms once per subscription, then create the virtual machine. Replace <your-vm-size> with the size you chose:
az vm image terms accept --urn cloudimg1647283583153:608f7ee7-2a42-4f52-893d-be662ec11828:608f7ee7-2a42-4f52-893d-be662ec11828:latest
az vm create \
--resource-group myResourceGroup \
--name my-rocky-8-vm \
--image cloudimg1647283583153:608f7ee7-2a42-4f52-893d-be662ec11828:608f7ee7-2a42-4f52-893d-be662ec11828:latest \
--size <your-vm-size> \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
The offer and the first plan are both named by the same identifier (608f7ee7-2a42-4f52-893d-be662ec11828), which is why it appears twice in the URN. For a Gen2 size, use the Gen2 plan instead, in both commands:
cloudimg1647283583153:608f7ee7-2a42-4f52-893d-be662ec11828:608f7ee7-2a42-4f52-893d-be662ec11828-gen2:latest
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-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.
The login message Activate the web console with: systemctl enable --now cockpit.socket comes from the cockpit-ws package on the image. The web console is not set up: the main cockpit package is not installed and the socket is disabled. Step 8 shows how to turn it on if you want it.
Step 3: Confirm the release
Check that the machine is the Rocky Linux 8.10 release you expect:
cat /etc/rocky-release
grep -E '^(NAME|VERSION|ID|PLATFORM_ID)=' /etc/os-release
uname -rm
Expected output on a fresh virtual machine from image 4.0.1:
Rocky Linux release 8.10 (Green Obsidian)
NAME="Rocky Linux"
VERSION="8.10 (Green Obsidian)"
ID="rocky"
PLATFORM_ID="platform:el8"
4.18.0-553.172.1.el8_10.x86_64 x86_64
The el8_10 tag in the kernel version shows the machine is on the 8.10 maintenance stream. After you apply updates (Step 4) and reboot, the kernel version moves on while the release stays 8.10.
Step 4: Check for and apply updates
dnf check-update returns exit code 100 when updates are pending and 0 when none are:
sudo dnf check-update > /dev/null 2>&1
echo "check-update exit code: $?"
dnf repolist --enabled
Expected output on a fresh virtual machine:
check-update exit code: 100
repo id repo name
appstream Rocky Linux 8 - AppStream
baseos Rocky Linux 8 - BaseOS
extras Rocky Linux 8 - Extras
The image was patched when it was built, so the exit code 100 lists only the updates published since then (on 2 November 2026 a fresh virtual machine had 37 waiting). sudo dnf history list shows that last build time upgrade as its newest entry. Apply the updates, and reboot if a new kernel was installed:
sudo dnf upgrade -y
sudo systemctl reboot
The image does not update itself. dnf-makecache.timer is enabled, but it only refreshes the repository metadata; dnf-automatic is not installed. To have updates applied on a schedule, install and enable it:
sudo dnf install -y dnf-automatic
sudo sed -i 's/^apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf
sudo systemctl enable --now dnf-automatic.timer
Step 5: Verify the security posture
Confirm SELinux is enforcing, root is locked and SSH is key only:
getenforce
grep -E '^[[:space:]]*SELINUX=' /etc/selinux/config
sudo passwd -S root | cut -d' ' -f1-2
sudo sshd -T | grep -E '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication) '
Expected output:
Enforcing
SELINUX=enforcing
root LK
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
root LK means the root password is locked. sudo sshd -T | grep '^permitrootlogin' prints permitrootlogin yes, from the PermitRootLogin yes line in /etc/ssh/sshd_config (the Rocky Linux 8 default), but root still cannot log in: its password is locked, and the only entry in /root/.ssh/authorized_keys is cloud-init's, which prints the "Please login as the user" message and closes the connection. To turn root login off in the SSH daemon as well, change that line:
sudo cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
sudo sed -i 's/^PermitRootLogin yes$/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart sshd
sudo sshd -T | grep '^permitrootlogin'
The last command then prints permitrootlogin no. Keep your current session open until you have checked that a new ssh azureuser@<vm-ip> still works.
Step 6: Verify Azure integration
Confirm the Azure Linux Agent, cloud-init and time synchronisation are healthy:
systemctl is-active waagent
systemctl is-enabled waagent
cloud-init --version
sudo cloud-init status
systemctl is-active chronyd
timedatectl | grep 'Time zone'
Expected output:
active
enabled
/usr/bin/cloud-init 23.4-7.el8_10.11.0.2
status: done
active
Time zone: UTC (UTC, +0000)
waagent is what lets Azure provision your SSH key, run extensions and report VM health, so it should always be active and enabled. chronyc sources shows PHC0, the Azure host clock, as the selected time source.
python3 --version reports Python 3.6.8. That is the platform Python that dnf itself uses; for application work, install a newer interpreter from AppStream alongside it, for example sudo dnf install -y python3.11.
The Azure CLI is not installed. To add it, use Microsoft's repository for RHEL 8 compatible systems:
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo dnf install -y https://packages.microsoft.com/config/rhel/8/packages-microsoft-prod.rpm
sudo dnf install -y azure-cli
az version
Sign in with az login before using it against your subscription.
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. The image needs a single inbound port, SSH on 22.
firewalld is not installed (rpm -q firewalld prints package firewalld is not installed), so your NSG rules take effect exactly as written. Confirm what is listening before you open anything:
ss -tlnu
On a fresh virtual machine you will see sshd on port 22 and rpcbind on port 111 (TCP and UDP), on all addresses, plus chronyd on 127.0.0.1:323 and [::1]:323, which only the machine itself can reach. Keep port 111 closed in your NSG. If you do not use NFS, switch rpcbind off:
sudo systemctl disable --now rpcbind.service rpcbind.socket
If your policy requires a host firewall as well, install and enable it, keeping SSH open:
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --list-services
--list-services then prints cockpit dhcpv6-client ssh: firewalld's default zone already allows SSH. Once it is running, a port must be open in both the NSG and firewalld before traffic reaches your service.
Step 8: Install software
AppStream carries the application packages. Search it, then install what you need:
dnf search nginx
The 8 series ships several versions of some languages, databases and servers as module streams, so you can pick a version rather than take the default:
dnf module list nginx
The list shows the streams from 1.14 [d] (the default) up to 1.24. Install a package and enable it in the usual way, for example sudo dnf install -y nginx followed by sudo systemctl enable --now nginx.
To use the web console the login message mentions, install it, enable the socket, and reach port 9090 over an SSH tunnel rather than opening it in the NSG:
sudo dnf install -y cockpit
sudo systemctl enable --now cockpit.socket
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. azureuser is the only login account until you add one:
getent group wheel
Filesystem Layout
| Mount Point | Description |
|---|---|
| / | Root filesystem (XFS, /dev/sda5) |
| /boot | Kernel files (XFS, /dev/sda2) |
| /boot/efi | EFI system partition (/dev/sda1) |
| /mnt | Azure temporary resource disk (ext4); its contents are lost when the VM is deallocated |
The OS disk is GPT partitioned with both an EFI system partition and a BIOS boot partition, which is how one image layout serves both the Gen1 and Gen2 plans.
Server Components
| Component | Version | Notes |
|---|---|---|
| Rocky Linux | 8.10 (Green Obsidian) | Maintenance support until 31 May 2029 |
| Azure Linux Agent | WALinuxAgent 2.7.0.6 | waagent, active and enabled |
| cloud-init | 23.4 | sudo cloud-init status |
| Python 3 | 3.6.8 | python3, the platform Python |
| chrony | from BaseOS | chronyd, active |
| firewalld | not installed | Step 7 shows how to add it |
| Azure CLI | not installed | Step 6 shows how to add it |
Common Use Cases
- RHEL 8 compatible application hosting
- Software certified on the Enterprise Linux 8 platform
- Long lived enterprise server workloads, within the 8 series support window
- A migration target for CentOS 8 estates
Troubleshooting
Cannot connect via SSH
- Verify the VM is in Running state in the Azure Portal
- Check that port 22 is allowed in the Network Security Group
- Ensure you are using the correct username:
azureuser, or the administrator name you chose - Verify your SSH key matches the one you supplied at create time. Password login is disabled, so a wrong key cannot fall back to a password prompt.
To see which key your client is actually offering, run ssh -v azureuser@<vm-ip> from your workstation and look for the Offering public key lines.
The VM does not start with my chosen size
Gen2 only sizes need the Gen2 plan, and the first plan needs a size that supports Gen1. Recreate the VM with the plan that matches your size (Step 1).
Azure agent not running
systemctl status waagent --no-pager
If it is not running, start and enable it with sudo systemctl enable --now waagent.
Package manager issues
Refresh the metadata cache, then check that the Rocky Linux mirrors resolve:
sudo dnf clean all
sudo dnf makecache
getent hosts download.rockylinux.org
A service is blocked and the logs mention SELinux
SELinux is enforcing on this image. Rather than disabling it, look at what was denied. On a fresh machine there are no recent denials, so the command reports none:
sudo ausearch -m AVC -ts recent 2>/dev/null || echo "no recent AVC denials"
Security Recommendations
- Apply the updates waiting on a new virtual machine (Step 4), and consider
dnf-automaticfor scheduled updates. - Allow SSH in the Network Security Group from your own address ranges only, and keep port 111 closed or switch rpcbind off (Step 7).
- Set
PermitRootLogin noif your policy requires it (Step 5). - The image keeps the installer's kickstart file
/root/original-ks.cfg(readable by root only). It records the build's installer settings, including a hashed installer password that no account on your virtual machine uses. You can remove it withsudo rm /root/original-ks.cfg.
Important Notes
Rocky Linux is free and open source, distributed under the BSD 3 Clause licence with GPL licensed components. No subscription or licence key is required.
Rocky Linux 8 is in maintenance support until 31 May 2029. Plan a move to a Rocky Linux 9 or 10 image before that date for workloads that need to outlive it.
Rocky Linux is a trademark of the Rocky Enterprise Software Foundation. The name is used here nominatively, only to identify the distribution this image contains. cloudimg is not affiliated with, endorsed by or sponsored by the Rocky Enterprise Software Foundation.
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