Deployment guide

Ubuntu 26.04 arm64

Overview

Ubuntu is a widely used Linux distribution produced by Canonical. Version 26.04, codenamed Resolute Raccoon, is a long term support (LTS) release with standard security maintenance to 29 May 2031. It gives you a stable base with a current kernel, toolchain and package set.

This image is the native 64-bit Arm (AArch64/arm64) build for Microsoft Azure. It runs on Azure's Arm virtual machine sizes, where many workloads deliver the same performance at a lower cost than on x86.

What you get:

  • Ubuntu 26.04 LTS, native 64-bit Arm (arm64 / AArch64)
  • Every available security update applied at build time, phased updates included
  • unattended-upgrades armed, so the machine keeps applying security updates on its own
  • Azure Linux Agent (walinuxagent) and cloud-init for Azure integration
  • chrony for NTP time synchronization, synchronized against the Azure host clock
  • AppArmor in enforcing mode
  • The standard Ubuntu archive (ports.ubuntu.com) enabled, so your update path stays intact
  • Gen2 Hyper V virtual machine support
  • 24/7 SecureAnyCloud support
  • Platform: Microsoft Azure (Gen2 Hyper V, Arm64)
  • Default user: azureuser

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.
  • PasswordAuthentication is disabled in the SSH daemon, so every login is by key.
  • PermitRootLogin is set to prohibit-password, so root cannot be reached with a password even if one is set later.
  • The only key that works is the public key you supply when you create the virtual machine. Azure injects it into azureuser at first boot, so no two machines you launch share a secret.
  • The machine identity and the SSH host keys are regenerated on every instance, so no two machines share a host key either.
  • AppArmor is left enabled and enforcing, which is the Ubuntu default.
  • Exactly one port listens for off box traffic: TCP 22 for SSH. Link local name resolution (LLMNR and mDNS) is pinned off, so the image opens no 5355 or 5353 listener.

Prerequisites

Before you deploy this image, you need:

  • 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) or larger. This is an Arm64 image, so you must launch it on an Arm virtual machine size: the Dpsv6/Dplsv6 families, or another p-suffixed Arm size. An x86 size such as Standard_B2s will not boot this image.
1

Deploy the Virtual Machine

Option A: Azure Portal

  • Sign in to the Azure Portal.
  • Select Create a resource and search the Marketplace for Ubuntu 26.04 LTS Arm64 by SecureAnyCloud.
  • Select Create.
  • On the Basics tab, choose your subscription and resource group, enter a virtual machine name, and choose a region that offers Arm sizes.
  • For Size, select See all sizes, filter on the Arm64 architecture and choose Standard_D2ps_v6 or larger.
  • Set Authentication type to SSH public key.
  • Set Username to azureuser and supply your public key.
  • On the Disks tab, choose your OS disk type. Standard SSD is a good default.
  • On the Networking tab, allow inbound SSH (22) from your own address range instead of the whole internet.
  • Select Review + create, then select Create.

Option B: Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:ubuntu-26-04-arm64:default:latest
az vm create \
  --resource-group myResourceGroup \
  --name myUbuntuArm64VM \
  --image sac1647283583153:ubuntu-26-04-arm64:default:latest \
  --size Standard_D2ps_v6 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

Use an Arm size. If you pass an x86 size, Azure rejects the deployment because the image is published for the Arm64 architecture.

2

Connect via SSH

ssh azureuser@<public-ip>

The image accepts key based logins only, so there is no password to enter or change.

3

Confirm the release and the architecture

Check that the machine runs the Ubuntu 26.04 LTS release you expect, and confirm that it runs on Arm:

grep -E '^(NAME|VERSION|ID|VERSION_ID|PRETTY_NAME)=' /etc/os-release uname -srm dpkg --print-architecture lscpu | grep -E '^Architecture|^Vendor ID|^Model name'

Expected output:

PRETTY_NAME="Ubuntu 26.04.1 LTS" NAME="Ubuntu" VERSION_ID="26.04" VERSION="26.04.1 LTS (Resolute Raccoon)" ID=ubuntu Linux 7.0.0-1012-azure aarch64 arm64 Architecture: aarch64 Vendor ID: ARM Model name: Neoverse-N2

aarch64 from uname and arm64 from dpkg both confirm that this is the native 64-bit Arm build, not an emulated or x86 image. lscpu names the physical Arm core you landed on. Neoverse-N2 is the core used by Azure's Cobalt 100 processor behind the Dpsv6 family. The /etc/os-release fields print in the order that the file stores them, which is why PRETTY_NAME appears first.

4

Check the patch level

The image is fully updated at build time. Confirm that no upgrades are pending and that the update path works:

apt-get -s -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade | grep -c '^Inst ' | sed 's/^/pending upgrades: /' apt-mark showhold dpkg -l | awk '/linux-image-[0-9]/{print $2, $3}'

Expected output shows pending upgrades: 0, no held packages, and exactly one installed Arm kernel:

pending upgrades: 0 linux-image-7.0.0-1012-azure 7.0.0-1012.12

apt-mark showhold prints nothing, which is the correct result because no package is pinned back. The Always-Include-Phased-Updates=true flag matters on Ubuntu. Without it, a plain check can hide phased security updates that are held back on this machine. Only one linux-image package is installed, because the build purges the superseded kernel and no stale kernel ships with the image. This image also arms unattended-upgrades, so security updates are downloaded and applied on a timer without any action from you. You can still run updates on your own schedule:

sudo apt-get update && sudo apt-get -y dist-upgrade

Reboot if a new kernel was installed:

sudo systemctl reboot
5

Verify the security posture

Confirm that AppArmor is enforcing, root is locked, and SSH is key only:

sudo aa-status --enabled && echo "apparmor: enabled"
sudo passwd -S root
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication) ' ss -tln | awk '$1=="LISTEN"{print $1, $4}' | sort
sudo ufw status | head -1

Expected output:

apparmor: enabled root L 2009-12-22 -1 -1 -1 -1 permitrootlogin prohibit-password pubkeyauthentication yes passwordauthentication no LISTEN 0.0.0.0:22 LISTEN 127.0.0.53%lo:53 LISTEN 127.0.0.54:53 LISTEN [::]:22 Status: inactive

OpenSSH 10, which ships with Ubuntu 26.04, reports this setting under its modern name prohibit-password. Ubuntu 24.04 and earlier printed the legacy synonym without-password for the same policy, so the output differs if you compare against an older machine. The meaning is the same: root may not authenticate with a password. The L in the passwd -S output is the part that matters. It means the root account is locked, so there is no root password to use. The 2009-12-22 date and the -1 aging fields that follow it are artifacts of the image build and Azure's deprovisioning step, not a record of a real password change. Every virtual machine launched from this image reports exactly those values.

Only port 22 is reachable from off the machine. The two entries on 127.0.0.53 and 127.0.0.54 are the local DNS stubs of systemd-resolved. They are bound to loopback addresses, so nothing outside the virtual machine can reach them. Ubuntu 26.04 binds two stubs instead of one. 127.0.0.53 is the normal stub and 127.0.0.54 is the bypass cache stub, and only the first carries the %lo interface suffix.

6

Verify Azure integration and unattended updates

Confirm that the Azure Linux Agent, cloud-init and time synchronization are healthy, that unattended security updates are armed, and check your resources:

systemctl is-active walinuxagent 2>/dev/null || systemctl is-active waagent cloud-init --version
systemctl is-enabled apt-daily-upgrade.timer apt-config dump | grep -E 'APT::Periodic::(Update-Package-Lists|Unattended-Upgrade) '
systemctl is-active chrony timedatectl show -p NTPSynchronized --value df -h / free -h

Expected output for the first five checks (the df and free figures depend on the size you deployed):

active /usr/bin/cloud-init 26.1-0ubuntu3~26.04.1 enabled APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; active yes

The Azure Linux Agent lets Azure provision your SSH key, resize the OS disk, run extensions and report VM health, so it should always be active. When apt-daily-upgrade.timer is enabled and the apt periodic config contains Unattended-Upgrade "1", the machine keeps applying security updates on its own after launch.

On Ubuntu 26.04, chrony handles time synchronization, not systemd-timesyncd as on Ubuntu 24.04 and earlier. systemd-timesyncd is not installed on this release, so systemctl is-active systemd-timesyncd reports inactive. This is expected and is not a fault. chrony synchronizes against the Azure host's precision clock, which it reports as the reference PHC0 at stratum 1:

chronyc tracking
7

Networking and the firewall

The Azure Network Security Group controls inbound and outbound traffic, and it is usually the only place where you need to open a port. The image advertises a single inbound port, SSH on 22.

ufw (the Uncomplicated Firewall) is installed on this image but inactive by default. It does not filter traffic, so your NSG rules take effect as written, with no second layer to keep in sync. Step 5 above lists what is listening: sshd on port 22, and nothing else that is reachable from off the machine.

Link local name resolution is pinned off in this image, so there is no listener on 5355 (LLMNR) or 5353 (mDNS). Confirm this yourself:

resolvectl status | grep 'Protocols:' | sed -n '1s/^[[:space:]]*//p'

Expected output:

Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported

The minus sign in front of LLMNR and mDNS means that both protocols are disabled. The command uses sed -n '1s/…//p' instead of grep -m1 on purpose. grep -m1 exits as soon as it matches, which sends SIGPIPE back to resolvectl and makes the pipeline return 141 under set -o pipefail, even though the line printed correctly.

If your policy also requires a host firewall, enable the one that is already installed. Add the SSH rule before you enable the firewall, so that you do not lock yourself out:

sudo ufw allow OpenSSH
sudo ufw allow 443/tcp
sudo ufw enable

Once ufw is running, open each port in both the Azure NSG and ufw before traffic can reach your service.

8

Install software

The image ships with the downloaded package lists cleared, which keeps it small. Refresh the lists first on a new machine. Until you do, apt-cache search returns nothing and apt-get install reports Unable to locate package:

sudo apt-get update apt-cache search nginx | sed -n '1,5p'

Expected output (the first few matches; the full list is longer):

libnginx-mod-http-geoip2 - GeoIP2 HTTP module for Nginx libnginx-mod-http-image-filter - HTTP image filter module for Nginx libnginx-mod-http-xslt-filter - XSLT Transformation module for Nginx

unattended-upgrades also refreshes the lists on its own timer, so this matters only in the first few minutes of a machine's life. Install a package and enable it in the usual way, for example sudo apt-get install -y nginx followed by sudo systemctl enable --now nginx.

Everything in the archive is built for Arm, so apt resolves arm64 packages automatically and you configure nothing extra for the architecture. Arm64 package coverage in the Ubuntu main and universe archives is effectively complete. Where a third party vendor ships only an x86 binary, use their Arm build instead.

To add another administrator, create the account with sudo adduser <name> and add it to the sudo group with sudo usermod -aG sudo <name>. Then copy your public key into /home/<name>/.ssh/authorized_keys, set mode 600 on the file and mode 700 on the .ssh directory, and make that user the owner of both. The image ships with no human accounts, so azureuser is the only login until you add one:

getent group sudo

Common Use Cases

  • Cost efficient general purpose compute. Web servers, application servers and API backends, where the Arm sizes usually deliver the same throughput for less money than the equivalent x86 size.
  • Cloud native and container workloads. arm64 container images are now standard across the major registries, so Kubernetes nodes and Docker hosts run natively.
  • Build and CI infrastructure. Native Arm build agents let you compile and test arm64 artifacts without emulation.
  • Data services. Databases, caches and message brokers that benefit from the memory bandwidth and consistent per core performance of the Arm sizes.
  • A modern LTS base image. A current kernel and toolchain with security maintenance to 2031, as the foundation for your own golden images.

Troubleshooting

Cannot connect via SSH

Check that the Azure NSG allows inbound TCP 22 from your address, that you connect as azureuser, and that you offer the private key that matches the public key you supplied at deployment:

ssh -v azureuser@<public-ip>

The image has password authentication disabled, so a password prompt never appears. If you see Permission denied (publickey), the key you offered is not the one Azure injected.

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 families and other p-suffixed sizes. If Azure rejected the deployment, or the machine never reaches the SSH stage, confirm that the size you selected is an Arm size. Confirm the architecture from inside the machine with:

uname -m

which must print aarch64.

Azure agent not running

sudo systemctl status walinuxagent

If the agent is not running, start it on the VM:

sudo systemctl enable --now walinuxagent

The agent handles key provisioning, disk resizing and extensions, so a stopped agent breaks Azure side management even though the machine itself keeps running. enable --now is safe to run against an agent that is already healthy, because it starts the unit only if the unit is stopped.

Package manager issues

Refresh the package lists and check that the archive is reachable:

sudo apt-get update apt-cache policy | head -20

The Arm build uses ports.ubuntu.com instead of archive.ubuntu.com. Both are Canonical's own mirrors, and no change is needed.

The clock is wrong, and systemd-timesyncd is not running

This is expected on this release. Ubuntu 26.04 uses chrony for time synchronization and does not install systemd-timesyncd. Check the service that is installed:

systemctl is-active chrony chronyc tracking

A service is blocked and the logs mention AppArmor

AppArmor is enforcing by default. Check for denials. This release has one known benign signature:

COUNT=$(sudo journalctl -k -b | grep 'apparmor="DENIED"' | grep -v 'profile="systemd-detect-virt"' | wc -l)
echo "unexpected apparmor denials: ${COUNT}"

Expected output on a healthy machine:

unexpected apparmor denials: 0

Ubuntu 26.04 logs three denials per boot from the systemd-detect-virt profile (two journal socket sends and one capable perfmon). This is a packaging defect in the stock 26.04 profile and has no functional effect. The check above filters out that one profile instead of counting every denial. A count of zero after that filter is the healthy result. An unfiltered count reports 3 on a healthy machine. The check uses wc -l instead of grep -c on purpose, because grep -c exits 1 when it counts zero matches, so the pipeline would report a non-zero status on a healthy machine. If your own service is denied, find the profile and put it into complain mode while you develop a policy:

sudo aa-complain /etc/apparmor.d/<profile>

Important Notes

  • This image ships no default credential: root is locked, password authentication is off, and no SSH key is baked in. Access is only by the key you supply at deployment.
  • SSH host keys and the machine ID are regenerated on every instance, so no two machines you launch share an identity.
  • Ubuntu 26.04 LTS receives standard security maintenance until 29 May 2031. Ubuntu Pro is an optional Canonical subscription that extends coverage further. It is not included with, not required by and not attached to this image. If you hold your own Canonical subscription, you can attach it with sudo pro attach <token>.
  • Ubuntu is free and open source, but it is a collection of thousands of independently licensed packages, not a single license. You can read each package's terms with dpkg -s <package> and under /usr/share/doc/<package>/copyright.
  • SecureAnyCloud provides this image. It is not affiliated with, endorsed by or sponsored by Canonical Ltd. "Ubuntu" is a trademark of Canonical Ltd., used here only to identify the distribution that the image contains.

Support

For help with this image, contact SecureAnyCloud support at sales@secureanycloud.com.

Deploy on Azure

Launch Ubuntu 26.04 LTS 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