Operating Systems Azure

openSUSE Leap 16 Arm64 on Azure User Guide

| Product: openSUSE Leap 16 Arm64 on Azure

Overview

This guide covers the deployment and configuration of openSUSE Leap 16.0 on 64-bit Arm (AArch64/arm64) on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace.

openSUSE Leap is a community distribution produced by the openSUSE Project, sharing its core codebase with SUSE Linux Enterprise 16. This is the native Arm build of openSUSE Leap 16.0, running the 6.12 kernel. 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:

  • openSUSE Leap 16.0, native 64-bit Arm (arm64 / AArch64) with the standard 4k page size kernel
  • Every available patch applied at build time, and again immediately before capture
  • Automatic security patching installed, armed and enabled, so the machine keeps patching itself
  • Azure Linux Agent (waagent) and cloud-init for Azure integration, with a first boot fix that cuts five minutes off every launch
  • Chrony for NTP time synchronisation against the Azure host clock
  • SELinux in enforcing mode, the Leap 16 default
  • The repo-oss update path enabled and GPG checked, so your patch 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 openSUSE Leap 16.0, on Arm

This listing tracks openSUSE Leap 16.0. openSUSE Leap 16.0 reached general availability on 1 October 2025 and is supported until 31 October 2027. That is a shorter support window than the Leap 15.x series offered, so plan your upgrade to the next Leap release before that date. The image is built from the current Leap 16.0 maintenance stream and brought fully up to date; your own zypper patch then rolls the machine forward within Leap 16.0 in the normal way.

Because this is the Arm build, it is a genuinely separate product from the x86 openSUSE Leap 16 image: it carries aarch64 packages and an aarch64 kernel, and it only runs on Arm virtual machine sizes. It uses a flat partition layout — a plain EFI system partition plus a root partition, with no LVM — and the standard 4k page size kernel.

A note on where this image comes from, because it matters for licensing: it is built from the official openSUSE Azure disk image published by the openSUSE Project, not from a third party Marketplace image. It therefore carries no Marketplace purchase plan of its own, and you do not need to accept any vendor terms beyond cloudimg's own to launch it.

What this image changes from the stock openSUSE Leap 16 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 five minute first boot stall is fixed. This is the big one. On the stock image, cloud-init's Azure data source asks dhcpcd for a temporary DHCP lease. dhcpcd gets the lease in about two seconds, but because cloud-init runs it with --persistent it never exits, so cloud-init sits and waits for its full 300 second timeout, logs Failed to obtain DHCP lease and an Azure data source failure, and only then reads the lease it already had. Boot succeeds, but every machine wastes five minutes doing it and cloud-init status reports degraded forever afterwards. This image installs the ISC dhclient, which exits once it has the lease, and tells cloud-init to prefer it. Measured on the build machine: boot time fell from 5 minutes 19 seconds to 10.2 seconds, and cloud-init reports done instead of degraded.
  • Automatic security patching is installed and armed. The stock image has no automatic update mechanism at all. openSUSE has no dnf-automatic and no unattended-upgrades, so this image ships a systemd timer, cloudimg-zypper-patch.timer, that runs zypper patch daily.
  • 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.
  • rpcbind is confirmed switched off, so nothing listens on port 111. Re-enable it with sudo systemctl enable --now rpcbind.socket if you need NFSv3.

The first boot change is recorded on the machine itself, at /var/lib/cloudimg/cloud-init-dhcp-client, together with the command that reverts it.

For the avoidance of doubt, two changes that cloudimg's Rocky Linux Arm images need are deliberately not made here, because the problems do not exist on this base: openSUSE Leap 16 ships no Cockpit web console (so nothing listens on port 9090) and no rng-tools (so nothing fails at boot for want of a hardware random number generator). The port check in Step 5 proves the result either way.

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.
  • 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 build account is deleted before capture, and the image is checked to confirm no account with a user ID of 1000 or above survives.
  • 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 openSUSE Leap 16 default.

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 "openSUSE Leap 16 Arm64 cloudimg"
  2. Select the image and click Create
  3. Choose your subscription, resource group and region
  4. Pick an Arm64 size such as Standard_D2ps_v6
  5. Set the administrator username and paste your SSH public key
  6. Allow inbound port 22 (SSH) and create the machine

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:opensuse-leap-16-arm64:default:latest

az vm create \
  --resource-group myResourceGroup \
  --name my-opensuse-leap-16-arm64-vm \
  --image cloudimg1647283583153:opensuse-leap-16-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

ssh azureuser@<your-vm-public-ip>

Only the key you supplied at create time will be accepted. There is no password login.

Step 3: Confirm the release and the architecture

Confirm you have genuine openSUSE Leap 16.0, built for Arm:

cat /etc/os-release
NAME="openSUSE Leap"
VERSION="16.0"
ID="opensuse-leap"
ID_LIKE="suse opensuse"
VERSION_ID="16.0"
PRETTY_NAME="openSUSE Leap 16.0"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:leap:16.0"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
DOCUMENTATION_URL="https://en.opensuse.org/Portal:Leap"
LOGO="distributor-logo-Leap"

Confirm the architecture and the processor. aarch64 is the Arm 64-bit architecture, and Neoverse-N2 is the core used by Azure Cobalt 100:

uname -m && lscpu | grep -E 'Architecture|Model name|BIOS Model'
aarch64
Architecture:                            aarch64
Model name:                              Neoverse-N2
BIOS Model name:                         Cobalt 100 None CPU @ 3.4GHz

Check the kernel and its page size. This image uses the standard 4k page kernel:

uname -r && getconf PAGESIZE
6.12.0-160000.37-default
4096

The terminal shows the os-release fields NAME openSUSE Leap, VERSION 16.0, ID opensuse-leap, VERSION_ID 16.0, PRETTY_NAME openSUSE Leap 16.0 and CPE_NAME cpe o opensuse leap 16.0, uname reporting the Linux 6.12.0 default kernel on aarch64 with a 4096 byte page size, lscpu reporting an ARM Neoverse-N2 Azure Cobalt 100 processor, a single installed kernel-default package, and the flat disk layout with a 512 MB EFI partition and a 29.5 GB btrfs root

The kernel is a packaged, managed kernel, so it receives patches through the normal update path:

rpm -q kernel-default
kernel-default-6.12.0-160000.37.1.aarch64

The disk layout

The image uses a flat GPT layout: an EFI system partition and a root partition, with no LVM. The root filesystem is btrfs, which is openSUSE's standard choice, with separate subvolumes for the directories that benefit from them:

lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT && findmnt -no SOURCE,FSTYPE /
NAME    SIZE FSTYPE MOUNTPOINT
sda      30G        
├─sda1  512M vfat   /boot/efi
└─sda2 29.5G btrfs  /var
sr0    1024M        

/dev/sda2[/@/.snapshots/1/snapshot] btrfs

The root filesystem being reported as a subvolume path is normal for openSUSE and is not a fault. To see the subvolumes:

sudo btrfs subvolume list /
ID 256 gen 11 top level 5 path @
ID 257 gen 16 top level 256 path @/.snapshots
ID 258 gen 63 top level 257 path @/.snapshots/1/snapshot
ID 259 gen 46 top level 256 path @/home
ID 260 gen 16 top level 256 path @/opt
ID 261 gen 55 top level 256 path @/root
ID 262 gen 16 top level 256 path @/srv
ID 263 gen 66 top level 256 path @/var
ID 264 gen 16 top level 256 path @/boot/writable
ID 265 gen 55 top level 256 path @/usr/local
ID 266 gen 16 top level 256 path @/boot/grub2/arm64-efi
ID 267 gen 16 top level 256 path @/boot/grub2/i386-pc

The operating system disk is 30 GB and carries no swap:

df -hT / /boot/efi && swapon --show
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/sda2      btrfs   30G  1.8G   28G   7% /
/dev/sda1      vfat   512M  4.3M  508M   1% /boot/efi

swapon --show prints nothing, which means no swap is configured. Azure image certification requires that, and you can add swap yourself if a workload needs it.

Step 4: Check the patch level and automatic updates

openSUSE uses zypper, not dnf or apt. The image was fully patched immediately before capture, so a freshly launched machine should report no pending patches:

sudo zypper patch-check
Loading repository data...
Reading installed packages...

0 patches needed (0 security patches)

zypper patch-check exits 0 when nothing is pending, 100 when ordinary patches are available and 101 when security patches are available, which makes it convenient in scripts.

Your update path is the repo-oss repository, with GPG checking enabled:

sudo zypper lr -E
# | Alias                  | Name                 | Enabled | GPG Check | Refresh
--+------------------------+----------------------+---------+-----------+--------
3 | openSUSE:repo-openh264 | repo-openh264 (16.0) | Yes     | (r ) Yes  | Yes
4 | openSUSE:repo-oss      | repo-oss (16.0)      | Yes     | (r ) Yes  | Yes

If you are used to Leap 15, note that Leap 16 does not have a separate repo-update. Maintenance patches are delivered through repo-oss itself, so the two repositories above are the whole update path.

Apply patches yourself at any time with:

sudo zypper refresh && sudo zypper patch

Automatic updates

openSUSE has no dnf-automatic and no unattended-upgrades, so this image ships a cloudimg systemd timer that runs zypper patch daily with a randomised delay of up to an hour:

systemctl is-enabled cloudimg-zypper-patch.timer && systemctl list-timers cloudimg-zypper-patch.timer --no-pager
enabled
NEXT                            LEFT LAST PASSED UNIT                        ACTIVATES
Tue 2026-09-15 00:06:31 UTC 1h 31min -         - cloudimg-zypper-patch.timer cloudimg-zypper-patch.service

1 timers listed.

To see what it will run, or to run it immediately:

systemctl cat cloudimg-zypper-patch.service
sudo systemctl start cloudimg-zypper-patch.service

To turn automatic patching off, if your change control requires it:

sudo systemctl disable --now cloudimg-zypper-patch.timer

Some patches need a reboot to take effect. Check with:

sudo zypper needs-rebooting

The terminal shows zypper patch-check reporting 0 patches needed and 0 security patches with exit code 0, zypper needs-rebooting reporting no reboot necessary, zypper lr listing repo-oss and repo-openh264 as enabled and GPG checked, the cloudimg-zypper-patch timer reported as enabled with its daily schedule and one hour randomised delay, cloud-init status reporting done rather than degraded, and systemd-analyze reporting a total boot time of ten seconds

Step 5: Verify the security posture

Confirm the effective SSH configuration. This reads the parsed, effective settings rather than the file, so it accounts for every drop in:

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

without-password is the older spelling of prohibit-password and means the same thing: root may not log in with a password.

Confirm the root account has no usable password:

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

The L means locked.

Confirm SELinux is enforcing:

getenforce
Enforcing

Confirm SSH is the only thing listening off the machine. This is worth checking on any image you run, not just this one:

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 terminal shows getenforce reporting Enforcing, passwd -S root reporting the root account as L meaning locked, no accounts holding a password hash, the effective sshd policy reporting permitrootlogin without-password, pubkeyauthentication yes, passwordauthentication no and kbdinteractiveauthentication no, id builder reporting no such user, swapon showing no swap, ss listing sshd on port 22 as the only TCP listener with chrony bound to the loopback address only, and rpcbind reported as disabled

Port 22 is the only port reachable from outside the machine. The two UDP sockets on port 323 are chrony's control interface, bound to the loopback address only, so they are not reachable from the network.

Step 6: Verify Azure integration

Confirm the Azure Linux Agent is present and running:

waagent --version
WALinuxAgent-2.14.0.1 running on opensuse 16.0
Python: 3.13.14
Goal state agent: 2.14.0.1

Confirm cloud-init completed cleanly. This is where the first boot fix shows up — on the stock image this reports degraded:

cloud-init status --long
status: done
extended_status: done
boot_status_code: enabled-by-generator
detail: DataSourceAzure [seed=/var/lib/waagent]
errors: []
recoverable_errors: {}

Check how long the machine took to boot:

systemd-analyze
Startup finished in 949ms (kernel) + 4.016s (initrd) + 5.244s (userspace) = 10.211s 

For comparison, the stock vendor image reaches the same point after 5 minutes and 19 seconds, because of the DHCP stall described earlier.

Confirm time synchronisation against the Azure host clock. PHC0 is the hypervisor clock, and stratum 1 means the machine is synchronised directly to it:

chronyc tracking
Reference ID    : 50484330 (PHC0)
Stratum         : 1
System time     : 0.000002129 seconds slow of NTP time

Confirm nothing failed during boot:

systemctl list-units --state=failed

An empty list is the expected result.

The Azure serial console works through the Arm PL011 UART, which is ttyAMA0 rather than the ttyS0 you would see on an x86 machine:

cat /proc/cmdline | tr ' ' '\n' | grep console=
console=tty1
console=ttyAMA0

The terminal shows waagent active and enabled at boot, WALinuxAgent version 2.14.0.1 running on opensuse 16.0, the cloud-init, chrony and dhcp-client package versions for aarch64, the console equals ttyAMA0 kernel argument for the Arm PL011 serial console, systemctl is-system-running reporting running with no failed units, chronyd synchronised against the Azure host clock PHC0, and df reporting the btrfs root and the EFI partition

Step 7: Networking and the firewall

No host firewall is installed or enabled on this image. Your Azure Network Security Group is the firewall, which is the usual arrangement for an Azure Linux image: manage inbound rules there and you have one place to look.

If you prefer a host firewall as well, install one:

sudo zypper install firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

Add that after you have confirmed SSH access, and always keep port 22 open in the rule set, or you will lock yourself out.

Step 8: Install software

Install packages with zypper:

sudo zypper refresh
sudo zypper install nginx

Search for a package:

zypper search nginx

Find which package owns a file:

zypper search --provides /usr/bin/python3

If you are coming from a Red Hat family distribution, the rough translations are dnf install to zypper install, dnf search to zypper search, dnf update to zypper update, and dnf upgrade --security to zypper patch.

Common Use Cases

  • A general purpose Linux server on Arm, where the lower cost of Azure Cobalt 100 is attractive
  • Build and test agents for software that ships aarch64 binaries or container images
  • Web and application servers, where the workload is already available for Arm
  • A development environment matching a SUSE Linux Enterprise 16 production target, since Leap 16 shares its core codebase

Troubleshooting

Cannot connect via SSH

Check the Network Security Group allows inbound TCP 22 from your address, that the machine is running, and that you are using the private key matching the public key you supplied at create time. Password authentication is disabled by design, so a password prompt means the key was not offered.

The VM will not create, or will not boot

The most common cause is an x86 virtual machine size. This is an Arm64 image and only runs on Arm sizes such as Standard_D2ps_v6. Confirm with uname -m, which must print aarch64.

Azure agent not running

sudo systemctl status waagent
sudo systemctl restart waagent

Package manager issues

Refresh the repository metadata first:

sudo zypper refresh

If a repository fails to refresh, confirm the machine has outbound internet access. If zypper reports a lock held by another process, the automatic patch timer may be running; wait for it to finish or stop it with sudo systemctl stop cloudimg-zypper-patch.service.

Locked plymouth packages

sudo zypper locks

openSUSE Leap 16 ships a vendor lock covering the plymouth boot splash packages. It is part of the stock distribution, it is cosmetic, and it does not hold back security patches — zypper patch-check reporting clean is the proof of that. Leave it in place.

A service is blocked and the logs mention SELinux

sudo ausearch -m AVC -ts recent

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

One message appears at error level on every boot:

journalctl -p err -b --no-pager
dhclient[...]: Timeout too large reducing to: 2147483646 (TIME_MAX - 1)

Azure's DHCP server hands out an unlimited lease time, and ISC dhclient logs a notice when it clamps that value to the largest number it can store. It is cosmetic, it does not affect the lease, and it is the direct consequence of the first boot fix described earlier — one harmless log line in exchange for five minutes off every boot. It is safe to ignore.

Important Notes

openSUSE Leap is free and open source. It is not covered by a single licence: the distribution is a collection of independently licensed packages. On this image, 550 installed packages carry 86 distinct licence strings, most commonly GPL-2.0-or-later, MIT, LGPL-2.1-or-later, GPL-3.0-or-later, BSD-3-Clause and Apache-2.0. You can read any package's terms with rpm -qi <package>. No subscription, entitlement or licence key is required, and no per seat cost applies. No SUSE entitlement is involved.

openSUSE is a trademark of SUSE. cloudimg is not affiliated with or endorsed by SUSE or the openSUSE Project, and uses the name only to identify the distribution this image contains.

openSUSE Leap 16.0 reached general availability on 1 October 2025 and is supported until 31 October 2027. Check the openSUSE Leap roadmap for the dates that apply to your deployment, and plan your upgrade to the next Leap release before support ends.

Support

For assistance with this image, contact cloudimg support: