
Testing Azure Workloads Locally With Azure Linux 4.0's Fedora-Based ISO
Running the Azure Linux 4.0 Host OS in a Local VM
Azure Linux used to be something you ran into sideways — a Marketplace image string buried in a Terraform file, or an /etc/os-release line you only noticed after SSH-ing in. If the report that Microsoft now ships Azure Linux 4.0 as a downloadable, Fedora-based ISO holds up, that changes how teams can build and test Azure workloads. This post walks through booting that ISO locally, verifying what the image actually contains, and deciding which workload tests are worth running before you pay for a deploy.
My position up front: the ISO is genuinely useful for pre-flight checks, and it is not evidence that a workload will behave the same way on a real Azure host. The gap is narrow in places and wide in others, and the wide parts are the ones that page you at 3am. A local boot is a fast filter, not an approval stamp.
A convention for this post: I did not have a verified Azure Linux 4.0 build in hand while writing it, so I am not going to paste kernel strings or package versions I never read. Code blocks below are labeled as commands or as expected output shape. Anything I could not verify is written as unknown rather than guessed.
What Microsoft Actually Announced for Azure Linux 4.0
The report I started from — NewsBytes, published 2026-10-04 — says Microsoft released Azure Linux 4.0 as an open-source, Fedora-based ISO available for direct download. That is the claim I am working from, and the only part I am treating as given.
Everything else has to come from the project itself. The package set, the kernel flavor, the release notes, the architecture targets, the support lifecycle — I would read all of that from the upstream repository and the official documentation, not infer it from the phrase "Fedora-based." I am deliberately not going to invent a kernel version or a glibc version to make this post look complete. If the project's own release notes do not state something, it is unknown, and "unknown" is more useful to you than a confident wrong number.
What the report does establish is a delivery change: an installable image you can pull down without going through Azure's image pipeline. That framing is the one worth thinking about, because it is the part that changes what teams can do.
Why the Delivery Change Matters More Than the Distro
"Microsoft ships Linux" stopped being news years ago. The interesting shift is that the host OS is now consumable outside the Azure image pipeline. Teams that previously had two options — deploy a Marketplace image into a real subscription, or approximate the host with a generic Fedora or CentOS Stream guest — now have a third: boot the actual thing locally, inspect it, and break it without a billing account.
| Approach | What it reproduces | What it cannot reproduce | Where it misleads |
|---|---|---|---|
| Azure Marketplace image | The real host OS build, platform agents, image-time hardening, the kernel as shipped | Your network path, your data, region rollout state, the cost of a bad deploy | It feels authoritative, so people skip the workload-side test entirely |
| Downloaded ISO in a local VM | Userspace package set, systemd unit layout, SELinux policy defaults, cloud-init boot path | Host hypervisor and host kernel, accelerated networking, IMDS behavior, platform patching cadence | Encourages "it booted, therefore it deploys" |
| Container base image | Your app's runtime dependencies, with fast iteration | Init system, kernel, SELinux, boot ordering, host agents | Hides everything outside the container boundary |
The second row is the new capability and the third row is the trap. If your "local reproducibility" story is a container, you are testing maybe a third of the surface the host OS actually exposes.
Booting the Azure Linux 4.0 ISO Locally
The workflow is boring on purpose. Pull the image, verify the checksum before you trust it, boot it with cloud-init attached so you get a login without clicking through an installer.
export ISO_URL="<paste the ISO URL from the project's release notes>"
export ISO="azurelinux-4.0-x86_64.iso"
curl -fL --retry 3 -o "$ISO" "$ISO_URL"
curl -fLO "$ISO_URL.sha256"
sha256sum -c "$ISO.sha256"
The line you want to see:
azurelinux-4.0-x86_64.iso: OK
If that line does not say OK, stop. A checksum failure is the cheapest possible place to learn that your download was truncated or that you are not pulling what you think you are pulling.
Then build a cloud-init seed and boot. cloud-localds comes from cloud-image-utils on Debian/Ubuntu hosts:
cloud-localds seed.iso seed/user-data seed/meta-data
virt-install \
--name azl4-preflight \
--memory 4096 --vcpus 2 \
--disk size=20,format=qcow2 \
--cdrom "$ISO" \
--cloud-init user-data=./seed/user-data,meta-data=./seed/meta-data \
--os-variant detect=on \
--graphics none --console pty,target_type=serial \
--noautoconsole
Record the host OS, the hypervisor version, and the ISO build date alongside whatever you observe. Results here are version-sensitive; a finding without that context is a rumor.
Verifying What the ISO Image Actually Ships
This is the part that matters, and it is where local testing can lie to you. Run these inside the guest and keep the output.
cat /etc/os-release
uname -r
rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' kernel glibc systemd
dnf repolist --all
systemctl is-enabled waagent cloud-init 2>&1
getenforce
Then read the output rather than skim it:
/etc/os-release— theIDandVERSION_IDare the ground truth for what you are actually running. IfIDis not what you expected, everything downstream is suspect.uname -r— the running kernel, which may not be the newest kernel package installed.rpm -q— whetherglibcandsystemdmatch what the release notes claim. A binary you build locally links against this glibc, and that is a classic way to ship something that only runs on your laptop.dnf repolist— which repositories are enabled. "Fedora-based" does not mean Fedora-package parity. Vendors rebuild and pin subsets, and the moment you pull a package from an upstream Fedora repo to unblock yourself, you have stepped off the supported surface without noticing.waagentand cloud-init enablement — whether Azure-specific userspace is present and expected to start at boot. This is what decides whether your local boot ordering resembles a real first boot.getenforce—EnforcingversusPermissivechanges which of your failures are real.
If a vendor value differs from what you assumed, the vendor value wins. That is the entire point of running the check.
Reproducing Azure-Only Behavior
Some things you can simulate locally and some you genuinely cannot.
Simulatable: the cloud-init boot path, systemd unit ordering and dependency resolution, host firewall defaults as shipped in the image, agent startup ordering, and a stand-in for IMDS by binding something to 169.254.169.254 inside the guest and serving plausible metadata JSON.
Not simulatable: physical host networking and accelerated networking, the host hypervisor and host-kernel behavior underneath your guest kernel, the platform's own patching cadence, and the platform's side of the shared-responsibility line.
A local IMDS stand-in is a mock. Any code path you "verify" against it is untested-against-production until you confirm it on a real Azure host. Mocks confirm your logic branches; they do not confirm platform behavior.
Testing a Real Workload, End to End
Take one small scoped service — a static HTTP responder, nothing with credentials or private endpoints.
sudo dnf install -y golang
mkdir -p ~/svc && cd ~/svc
cat > main.go <<'EOF'
package main
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ok")
})
http.ListenAndServe("127.0.0.1:8080", nil)
}
EOF
go build -o svc . && sudo install -m 0755 svc /usr/local/bin/svc
Then run it under a unit and check the result:
sudo systemctl start svc && curl -s http://127.0.0.1:8080/healthz
Output you want to see:
ok
That passes and tells you very little, which is the useful lesson. Now trigger the failure that only exists because this guest is not the production host: bind the service to the host's external interface instead of loopback and watch the shipped firewall policy decide the outcome. Whatever you observe locally is the image's default, not your platform's enforced default. The second failure I look for is an agent or unit that waits on IMDS at boot. Locally, that dependency has nothing to talk to, so the unit either times out or starts late. On a real host it resolves in milliseconds and the ordering bug you just found disappears — or reappears somewhere worse. Both are worth writing into the release checklist.
Failure Modes I Expect Before You Hit Them
| Failure mode | Why it bites | Status |
|---|---|---|
| Upstream Fedora drift vs. the vendor's rebuilt set | You unblocked yourself with an upstream package; production runs the vendor's pinned build | Inferred |
| Kernel or glibc mismatch | A binary built against local glibc does not load on the production host | Inferred, well-documented class |
| SELinux denials that pass locally | Local guest is permissive; production policy is stricter | Inferred |
| Container base-image mismatch | The container hid the host differences you were trying to test | Inferred |
| "Works in QEMU" boot assumptions | Two vCPUs and no accelerated networking is not a production host | Inferred |
| IMDS-dependent startup | Nothing to resolve at 169.254.169.254 locally | Failed to reproduce |
Defense: Making Local Fidelity Earn Its Place
These are rules I would actually enforce on a team, not aspirations.
- Pin the ISO by build hash, never "latest." Record the hash and the download date next to the test result. A finding you cannot date is not a finding.
- Verify the checksum and the repo list in CI. Not in a wiki page. If the repo list changes, the test should notice.
- Keep a base-image matrix. Local ISO, Marketplace image, container base. Mark which tests run against which, and never let the local guest hold authority over a version number.
- Keep the Azure-side integration test as the release gate. The local VM is a pre-filter that catches fast, cheap failures. It does not sign off.
- Write down what the local guest cannot answer. A one-line list in the repo is enough, and it stops the next person from treating a green local run as coverage.
This is a workflow opinion and I will defend the reasoning: a local boot costs minutes, and a bad deploy costs hours of rollback plus whatever audience watched it happen. Paying minutes to catch the cheap failures is obviously correct. The danger is not the local VM — it is the team that lets the local VM become the last test before production.
What I Confirmed and What I Did Not Test
Confirmed by the report: Microsoft released Azure Linux 4.0 as an open-source, Fedora-based ISO available for direct download (NewsBytes, 2026-10-04).
Not tested by me, and therefore unknown:
- The kernel version, glibc version, and package set in any 4.0 build
- The exact repository set enabled in the image
- Whether any specific IMDS-dependent code path resolves differently on a real host
- The architecture targets and support lifecycle
I also want to be explicit about one failure: I could not reproduce an IMDS-dependent startup path locally at all, because there is no platform IMDS on a laptop. Any claim that "the service starts fine here" is therefore partial by construction, and I would rather say that than imply full coverage.
Further Reading
- The NewsBytes report on the Azure Linux 4.0 ISO — the source claim for this post
- Azure Linux upstream repository — release notes, image definitions, and package manifests
- Azure Linux documentation — Microsoft's own docs for the Azure host OS
- Azure Instance Metadata Service — what IMDS actually provides and how it is reached
- cloud-init documentation — datasources and boot-stage behavior
- Fedora packaging guidelines — the upstream rules a Fedora-based vendor set diverges from
Closing Position
A downloadable, inspectable host OS ISO raises the floor for pre-deploy testing, and I would adopt it for exactly that. It does not shrink the production trust boundary: the host hypervisor, the networking fabric, the platform's patching cadence, and IMDS all stay on Microsoft's side of the line. A team that treats a successful local boot as deploy approval has not removed risk — it has just moved the failure earlier, where it is cheaper to find and easier to ignore.


