Deployment guide

AlmaLinux 10 Lvm

Overview

AlmaLinux is a community-owned enterprise Linux distribution, produced by the AlmaLinux OS Foundation to be binary compatible with Red Hat Enterprise Linux. AlmaLinux 10 runs the 6.12 kernel and follows the RHEL 10 lifecycle.

This SecureAnyCloud image puts the root filesystem on an LVM logical volume inside a volume group, instead of on a fixed partition. You can grow it online, add capacity from new disks and take snapshots with the standard LVM tools.

AlmaLinux does not publish an LVM image of its own. SecureAnyCloud builds this one from the official AlmaLinux 10 installation media with an automated installation, using the same package set and Azure configuration as the standard AlmaLinux 10 Azure image. The only intended difference is the disk layout.

What you get:

  • AlmaLinux 10 (x86_64), binary compatible with RHEL 10
  • Root filesystem on an LVM logical volume (volume group almalinux, logical volume root, XFS), with /boot and the EFI system partition as plain partitions
  • The standard AlmaLinux 10 Azure image's package set, checked package for package
  • 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, CRB and Extras repositories enabled, so your update path is intact
  • SSH as the only network listener, with no swap on the operating system disk and no swap volume
  • Gen2 Hyper-V virtual machine support
  • 24/7 SecureAnyCloud support

Platform: Microsoft Azure (Gen2 Hyper-V, x64). Default user: azureuser.

This is the AlmaLinux 10 major release

This listing tracks the AlmaLinux 10 major release, not a frozen minor. RHEL-family security fixes roll into the latest minor stream. The image is therefore built from whichever AlmaLinux 10 point release is current at build time and brought fully up to date.

Your own dnf update rolls the machine forward within AlmaLinux 10 in the normal way, so you keep receiving security fixes. AlmaLinux 10 has active support until May 2030 and security support until May 2035.

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 no, so root cannot log in over SSH at all. 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 AlmaLinux 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_B2s (2 vCPU, 4 GB RAM) or larger.

1

Deploy the Virtual Machine

Option A: Azure Portal

  • Open the Azure Marketplace and search for "AlmaLinux 10 LVM SecureAnyCloud".
  • Select the image and choose Create.
  • On the Basics tab, configure the following:
    • 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 your preferred region
    • Size: Standard_B2s is recommended
  • Under Administrator Account, select SSH public key and enter your key.
  • Under Disks, keep the default 30 GB operating system disk or choose a larger size. Step 4 shows how to claim the extra space.
  • Under Inbound Port Rules, allow SSH (port 22).
  • Select Review + Create, then Create.

Option B: Azure CLI

Run these commands on your own workstation. Substitute 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:almalinux-10-lvm:default:latest
az vm create \
  --resource-group myResourceGroup \
  --name my-almalinux-10-lvm-vm \
  --image sac1647283583153:almalinux-10-lvm:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Add --os-disk-size-gb 64 (or any size above 30) to start with a larger operating system disk.

2

Connect via SSH

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

az vm show --resource-group myResourceGroup --name my-almalinux-10-lvm-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 at create time.

3

Confirm the release

Check that the machine is AlmaLinux 10:

cat /etc/almalinux-release
AlmaLinux release 10.2 (Lavender Lion)

The os-release fields carry the same information in a machine-readable form, which configuration management tools read:

grep -E '^(NAME|VERSION|ID|VERSION_ID|PLATFORM_ID)=' /etc/os-release
NAME="AlmaLinux" VERSION="10.2 (Lavender Lion)" ID="almalinux" VERSION_ID="10.2" PLATFORM_ID="platform:el10"

The distribution itself asserts its RHEL compatibility lineage, and the kernel is the AlmaLinux 10 build for x86_64:

cat /etc/redhat-release uname -srm
AlmaLinux release 10.2 (Lavender Lion) Linux 6.12.0-211.53.1.el10_2.x86_64 x86_64

AlmaLinux 10 for x86_64 is compiled for the x86-64-v3 processor level, which every current Azure x64 size provides. Ask the system loader which levels your processor supports:

/lib64/ld-linux-x86-64.so.2 --help | grep -E 'x86-64-v[234]'
x86-64-v4 (supported, searched) x86-64-v3 (supported, searched) x86-64-v2 (supported, searched)

The top line depends on the processor behind your VM size. AlmaLinux 10 needs x86-64-v3 (supported, searched).

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

4

The LVM disk layout

This layout is what sets the image apart. The root filesystem sits on an LVM logical volume inside a volume group, not on a fixed partition. The /boot and EFI system partitions stay as plain partitions, which is what the firmware and boot loader need:

df -hT / /boot /boot/efi
Filesystem Type Size Used Avail Use% Mounted on /dev/mapper/almalinux-root xfs 29G 1.5G 28G 6% / /dev/sda2 xfs 960M 244M 717M 26% /boot /dev/sda1 vfat 200M 9.1M 191M 5% /boot/efi

One physical volume, the third partition of the operating system disk, backs the volume group almalinux. The volume group holds a single logical volume, root. There is no swap volume:

sudo pvs
sudo vgs
sudo lvs -o lv_name,vg_name,lv_size,lv_path,devices
PV VG Fmt Attr PSize PFree /dev/sda3 almalinux lvm2 a-- 28.80g 0 VG #PV #LV #SN Attr VSize VFree almalinux 1 1 0 wz--n- 28.80g 0 LV VG LSize Path Devices root almalinux 28.80g /dev/almalinux/root /dev/sda3(0)

The kernel command line tells the early boot environment which logical volume holds the root filesystem. The LVM, partition-growing and XFS tools are all installed:

grep -o 'root=[^ ]*\|rd.lvm[^ ]*' /proc/cmdline rpm -q lvm2 cloud-utils-growpart xfsprogs
root=/dev/mapper/almalinux-root rd.lvm.lv=almalinux/root lvm2-2.03.36-2.el10.x86_64 cloud-utils-growpart-0.33-11.el10.noarch xfsprogs-6.16.0-1.el10.x86_64

The same rd.lvm.lv=almalinux/root argument is recorded in /etc/kernel/cmdline, so every kernel that a future update installs also boots from the logical volume.

Growing the root filesystem

The volume group ships with no free space, and enlarging the Azure disk does not grow the root volume on its own. cloud-init's automatic partition growth deliberately skips a root filesystem on a logical volume, so the extra capacity stays unallocated until you claim it.

First, enlarge the disk. Either create the machine with a larger disk (--os-disk-size-gb), or resize the disk in Azure: stop and deallocate the VM, raise the size, then start it again. Then run the block below. Every step works online, with no reboot, and the last lines confirm that the whole chain took effect:

sudo growpart /dev/sda 3
sudo pvresize /dev/sda3
sudo lvextend -l +100%FREE /dev/almalinux/root
sudo xfs_growfs / df -h /
FREE=$(sudo vgs --noheadings -o vg_free_count almalinux | tr -dc '0-9')
PVB=$(sudo pvs --noheadings --units b --nosuffix -o pv_size /dev/sda3 | tr -dc '0-9')
PARTB=$(( $(cat /sys/class/block/sda3/size) * 512 )) if [ "${FREE:-1}" -eq 0 ] && [ "$(( PARTB - PVB ))" -le 8388608 ]; then echo "root volume fully extended: 0 free extents in VG almalinux, and the physical volume fills the partition"; else echo "NOT fully extended: ${FREE:-?} free extents in VG almalinux, partition ${PARTB} B vs physical volume ${PVB:-?} B"; false; fi

Each command does one job:

  • growpart extends partition 3 to the end of the disk.
  • pvresize makes LVM see the larger physical volume.
  • lvextend hands all the free space to the root logical volume.
  • xfs_growfs grows the filesystem into it.

On a machine created with a 64 GB disk, the output looks like this:

CHANGED: partition=3 start=2508800 old: size=60403712 end=62912511 new: size=131708895 end=134217694 Physical volume "/dev/sda3" changed 1 physical volume(s) resized or updated / 0 physical volume(s) not resized Size of logical volume almalinux/root changed from 28.80 GiB (7373 extents) to 62.80 GiB (16077 extents). Logical volume almalinux/root successfully resized. meta-data=/dev/mapper/almalinux-root isize=512 agcount=4, agsize=1887488 blks ... data blocks changed from 7549952 to 16462848 Filesystem Size Used Avail Use% Mounted on /dev/mapper/almalinux-root 63G 2.2G 61G 4% / root volume fully extended: 0 free extents in VG almalinux, and the physical volume fills the partition

If the disk has not been enlarged, growpart prints NOCHANGE and lvextend reports that the size already matches. That is expected, and the final check still confirms that the volume fills the disk.

The two checks at the end catch the two ways a grow can silently stop short. Free extents left in the volume group mean lvextend did not run. A physical volume smaller than its partition means pvresize did not. Confirm the physical volume name with sudo pvs first. It is /dev/sda3 on this image.

Adding a data volume

Attach a new managed data disk in Azure. The portal and CLI show its LUN, and the matching device appears as /dev/disk/azure/data/by-lun/<N>. The older /dev/disk/azure/scsi1/lun<N> name works too. Give the disk its own volume group to keep application data separate from the operating system:

sudo pvcreate /dev/disk/azure/data/by-lun/0
sudo vgcreate datavg /dev/disk/azure/data/by-lun/0
sudo lvcreate -n datalv -l 100%FREE datavg
sudo mkfs.xfs /dev/datavg/datalv
sudo mkdir -p /data echo '/dev/datavg/datalv /data xfs defaults,nofail 0 0' | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount /data

The nofail option lets the machine still boot if the data disk is ever detached. To add the disk's capacity to the root volume instead, run sudo vgextend almalinux /dev/disk/azure/data/by-lun/0, then repeat the lvextend and xfs_growfs steps above.

The image ships /etc/lvm/devices empty, exactly like the standard AlmaLinux 10 Azure image. LVM therefore sees every attached disk, and a machine cloned from this image carries no record of the build machine's disks.

5

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

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.

One kernel version is installed, the one the machine is running. Like the standard AlmaLinux 10 Azure image, it also carries the debug build of that same kernel. You can select the debug build from the boot menu when you diagnose a kernel problem. The machine boots the regular kernel by default:

rpm -q kernel-core kernel-debug-core
sudo grubby --default-kernel
kernel-core-6.12.0-211.53.1.el10_2.x86_64 kernel-debug-core-6.12.0-211.53.1.el10_2.x86_64 /boot/vmlinuz-6.12.0-211.53.1.el10_2.x86_64

Your update path is intact. BaseOS, AppStream, CRB (CodeReady Builder) and Extras are all enabled, which is the AlmaLinux 10 cloud image default. List them:

dnf repolist --enabled
repo id repo name appstream AlmaLinux 10 - AppStream baseos AlmaLinux 10 - BaseOS crb AlmaLinux 10 - CRB extras AlmaLinux 10 - Extras

Automatic updates

The standard AlmaLinux 10 Azure image does not include dnf-automatic. This image installs it and enables its timer, so the machine applies updates on its own schedule. Check the timer:

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. Where the metadata is missing, it silently applies nothing, so default is the setting that actually keeps the machine current.

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

6

Verify the security posture

Confirm SELinux is enforcing:

getenforce
Enforcing

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

sudo 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 instead of the config file, because 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

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:

sudo grep -vE '^#' /etc/ssh/sshd_config.d/00-sac-hardening.conf
PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PermitEmptyPasswords no

There is no swap on the operating system disk and no swap logical volume, because swap baked into a Marketplace image is not permitted. This command 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 image ships nfs-utils for NFS version 4 client mounts, which need no extra listener. The rpcbind service that NFS version 3 relies on is installed but disabled, so port 111 stays closed:

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

If you need NFS version 3, enable the service with sudo systemctl enable --now rpcbind.socket. Open only the ports you need in your Network Security Group.

7

Verify Azure integration

The Azure Linux Agent handles extensions and the portal's recovery features. cloud-init provisions the machine. Check both:

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.noarch chrony-4.8-2.el10.x86_64

cloud-init should report done, which means provisioning finished cleanly:

cloud-init status
status: done

The kernel puts a console on ttyS0. That console makes boot diagnostics and the portal's serial console work when you cannot reach the machine over SSH:

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

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 10 -58us[ -68us] +/- 11us

On sizes with a local temporary disk, such as Standard_B2s, cloud-init formats the disk and mounts it at /mnt. Treat anything on it as disposable, because Azure erases it whenever the machine is deallocated or moved:

findmnt -no SOURCE,FSTYPE,TARGET /mnt
/dev/sdb1 ext4 /mnt
8

Networking and the firewall

The Azure Network Security Group 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 standard AlmaLinux 10 Azure image. With no host firewall in the way, your NSG rules take effect exactly as written, and there is 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 can reach your service.

9

Install software

The AppStream repository carries the application packages. Check that a package such as nginx is available:

dnf -q list --available nginx 2>/dev/null | tail -2
Available Packages nginx.x86_64 2:1.26.3-6.el10_2.6 appstream

Install a package and enable it in the usual way. For example, run sudo dnf install -y nginx, then sudo systemctl enable --now nginx, then open the port in your Network Security Group. Application data that grows, such as databases and web content, fits well on its own logical volume on a data disk (see Step 4).

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. Set mode 600 on the file and mode 700 on the .ssh directory, both 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. List the members of that group with:

getent group wheel

Common Use Cases

  • RHEL 10-compatible servers whose storage needs to grow online without repartitioning
  • Database and application hosts that keep data on separate logical volumes and data disks
  • Environments whose build standards or compliance baselines require LVM-managed storage
  • A consistent LVM base for 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 confirm that the machine is running. Make sure your client offers the private key that matches the public key you supplied at create time.

Password authentication is disabled, so a password prompt means key negotiation has 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-10-lvm-vm --show-details --query powerState -o tsv

The VM does not come back after a change

Open Boot diagnostics and the Serial console for the VM in the portal. Both read from ttyS0.

Suppose the console stops at the early boot shell and reports that /dev/mapper/almalinux-root does not exist. The kernel was then started without the rd.lvm.lv=almalinux/root argument, which usually means a custom kernel command line replaced the defaults. Every boot entry on this image carries the argument. Check yours with sudo grubby --info=ALL | grep args.

The root filesystem did not grow after resizing the disk

This is expected on an LVM root. Run the grow block in Step 4. If growpart still prints NOCHANGE, confirm that the resize reached the VM with sudo pvs and cat /sys/class/block/sda/size. Azure applies a disk resize only after the VM has been deallocated.

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 that the machine has outbound HTTPS and that DNS resolves. 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 instead of 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.

Important Notes

AlmaLinux is free and open source. A single licence does not cover it. 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. SecureAnyCloud is not affiliated with or endorsed by the AlmaLinux OS Foundation, and uses the name only to identify the distribution this image contains.

AlmaLinux 10 follows the RHEL 10 lifecycle: active support runs until 31 May 2030 and security support until 31 May 2035. Check the AlmaLinux release notes for the dates that apply to your deployment.

Support

For help 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 AlmaLinux 10 LVM 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