Networking Azure

netboot.xyz on Ubuntu 24.04 on Azure User Guide

| Product: netboot.xyz on Ubuntu 24.04 LTS on Azure

Overview

This image runs netboot.xyz 3.0.2 on Ubuntu 24.04 LTS. netboot.xyz is an open source network boot toolkit: an iPXE bootloader paired with a curated menu tree covering well over a hundred operating systems and utilities. Point a machine's network boot at this server and it boots straight into a live desktop, a rescue shell, a disk utility or a full OS installer - no USB media, no walking to the rack.

This is the official netboot.xyz local instance, which upstream ships only as a container. cloudimg packages it natively on Ubuntu 24.04 with real systemd units and no container layer. Running your own instance means your machines boot from infrastructure you control, they keep working when your internet link does not, and you can mirror the images you care about locally so provisioning runs at LAN speed instead of pulling gigabytes from the internet for every machine.

The appliance has two planes, and they deliberately have different security postures:

  • The management plane - the netboot.xyz web admin UI, which decides what your machines boot. It is authenticated, with a password generated uniquely on your VM's first boot.
  • The boot plane - TFTP on port 69/udp plus the menus and mirrored assets over HTTP. It is anonymous, because a machine that is powering on has no way to present credentials. You scope it with your Azure Network Security Group.

What is included:

  • netboot.xyz 3.0.2: the iPXE menu tree (104 menus) plus all ten official bootloaders (BIOS, UEFI, Secure Boot, arm64 and legacy variants), each verified against a pinned sha256 at build time
  • The netboot.xyz web application 0.7.6 on Node.js 22 LTS, bound to 127.0.0.1:3000 and reachable only through the authenticating reverse proxy
  • nginx on port 80: the admin UI at / behind HTTP Basic Auth, the boot menus at /menus/, the local asset mirror at /assets/, and an unauthenticated /healthz for Azure Load Balancer probes
  • dnsmasq serving TFTP only on port 69/udp (its DNS listener is disabled)
  • netbootxyz-web.service, netbootxyz-tftp.service, nginx.service and a one-shot netbootxyz-firstboot.service, all enabled
  • A per-VM admin password generated on first boot and written to /root/netbootxyz-credentials.txt, readable only by root. No shared or default login exists in the image
  • A fully patched Ubuntu 24.04 LTS base with unattended security upgrades enabled
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size. If you plan to mirror many installer images locally, attach and mount a data disk for /assets - the image itself is small, but mirrored images are large.

NSG inbound rules: allow 22/tcp from your management network, 80/tcp from the networks that need the admin UI and HTTP boot, and 69/udp from the subnets whose machines will network boot. Do not expose 69/udp or 80/tcp to the internet. The boot plane is anonymous by design, so the NSG is what scopes it.

Network booting requires your clients to be on a network whose DHCP server you can configure (to set next-server at this VM), and to be in the same broadcast domain as that DHCP server. This is why the appliance is typically deployed into the VNet that serves your machines, or reached from on premises over a VPN or ExpressRoute link.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for netboot.xyz by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create then Create. Add the 69/udp TFTP rule after deployment, scoped to your client subnets.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name netbootxyz \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

# Admin UI + HTTP boot, scoped to the networks that need it
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name allow-http --priority 1010 --protocol Tcp --destination-port-ranges 80 \
  --source-address-prefixes <your-mgmt-cidr>

# TFTP for network boot, scoped to your client subnets
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name allow-tftp --priority 1020 --protocol Udp --destination-port-ranges 69 \
  --source-address-prefixes <your-client-subnet-cidr>

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Three services make up the appliance: the web admin application, the TFTP server and nginx. Confirm all three are active and check what is listening.

Note the :3000 line: the admin application is bound to 127.0.0.1, not 0.0.0.0. That is deliberate - it means the admin UI can only be reached through nginx, which enforces authentication. It cannot be bypassed by connecting to port 3000 directly.

systemctl is-active netbootxyz-web netbootxyz-tftp nginx
sudo ss -tlnp | grep -E ':80 |:3000 '
sudo ss -ulnp | grep ':69 '

Expected output:

active
active
active
LISTEN 0      511        127.0.0.1:3000      0.0.0.0:*    users:(("node",pid=...))
LISTEN 0      511          0.0.0.0:80        0.0.0.0:*    users:(("nginx",pid=...))
LISTEN 0      511             [::]:80           [::]:*    users:(("nginx",pid=...))
UNCONN 0      0            0.0.0.0:69        0.0.0.0:*    users:(("dnsmasq",pid=...))
UNCONN 0      0               [::]:69           [::]:*    users:(("dnsmasq",pid=...))

The three netboot.xyz services active, with the admin app bound to 127.0.0.1:3000, nginx on port 80 and dnsmasq serving TFTP on port 69

Step 5 - Retrieve your per-VM admin password

The netboot.xyz web application has no login of its own upstream. Because that application is what decides which operating systems your machines boot, this image puts it behind HTTP Basic Auth and generates the password on your VM, on its first boot. Nothing in the image knows it and no other VM shares it.

Read it from the root-only credentials file:

sudo cat /root/netbootxyz-credentials.txt

Expected output (your password will differ - it is unique to your VM):

# netboot.xyz on Ubuntu 24.04 LTS (cloudimg Azure Marketplace image)
#
# SECURE BY DEFAULT: the password below was generated on THIS VM at first boot and is
# unique to it. It is not baked into the image and no other VM shares it.

netbootxyz_admin_user=admin
netbootxyz_admin_password=<generated on your VM at first boot>
NETBOOTXYZ_URL=http://<vm-ip>/

The per-VM netboot.xyz credentials file, showing the admin user and the generated password redacted, plus the boot server URLs

The file records the VM's own address. When reaching the admin UI from your workstation, use the VM's public IP or DNS name.

Step 6 - Confirm the boot server is serving

The boot plane is what your machines actually talk to. Confirm TFTP serves the iPXE bootloader, the menus are reachable over HTTP for UEFI HTTP boot, and the asset mirror answers:

curl -s -o /tmp/probe.kpxe tftp://127.0.0.1/netboot.xyz.kpxe -w 'tftp netboot.xyz.kpxe -> %{size_download} bytes\n'
rm -f /tmp/probe.kpxe
curl -s -o /dev/null -w 'http /menus/menu.ipxe -> HTTP %{http_code}\n' http://127.0.0.1/menus/menu.ipxe
curl -s -o /dev/null -w 'http /assets/ -> HTTP %{http_code}\n' http://127.0.0.1/assets/
printf 'menu version: '; cat /config/menuversion.txt; printf '\niPXE menus: '; ls -1 /config/menus/*.ipxe | wc -l

Expected output:

tftp netboot.xyz.kpxe -> 410421 bytes
http /menus/menu.ipxe -> HTTP 200
http /assets/ -> HTTP 200
menu version: 3.0.2
iPXE menus: 104

TFTP serving the netboot.xyz bootloader, the menus and asset mirror answering over HTTP, and the shipped menu tree at version 3.0.2 with 104 iPXE menus

The /assets/ index is empty until you mirror something into it (Step 11). The boot menus work immediately regardless, pulling from the official upstream mirrors.

Step 7 - Confirm the admin UI is protected

Prove the authentication is real: a request with no credentials must be rejected, and the per-VM password must be accepted. This reads the password straight out of the credentials file, which is also the pattern to use in your own scripts:

PW=$(sudo awk -F= '/^netbootxyz_admin_password=/{print $2; exit}' /root/netbootxyz-credentials.txt)
curl -s -o /dev/null -w 'admin UI, no credentials   -> HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'admin UI, with credentials -> HTTP %{http_code}\n' -u "admin:$PW" http://127.0.0.1/
curl -s -o /dev/null -w 'healthz (anonymous probe)  -> HTTP %{http_code}\n' http://127.0.0.1/healthz

Expected output:

admin UI, no credentials   -> HTTP 401
admin UI, with credentials -> HTTP 200
healthz (anonymous probe)  -> HTTP 200

The admin UI returning 401 without credentials and 200 with the per-VM credentials, and the anonymous health endpoint returning 200

/healthz is intentionally anonymous so an Azure Load Balancer can probe the VM without credentials. It returns only the string ok and exposes nothing.

Step 8 - Open the admin dashboard

Browse to http://<vm-public-ip>/ and sign in with admin and the password from Step 5. The dashboard reports the webapp and menu versions, whether the menus are up to date, and the TFTP and web server backing the appliance - here dnsmasq and nginx, the native services this image runs.

The netboot.xyz dashboard showing webapp version 0.7.6, menus version 3.0.2 up to date, dnsmasq serving TFTP, nginx as the web server, and live CPU and memory stats

Step 9 - Browse the boot menus

Select Menus to see the iPXE menu tree. The sidebar lists every menu file shipped with release 3.0.2 - one per operating system or utility family, plus boot.cfg, the global configuration every menu reads. You can also create your own menu file here with Create New.

The netboot.xyz Menus view listing the shipped iPXE menu files including boot.cfg, with the option to create a new menu file

Step 10 - Edit a menu file

Select any file to open it in the built in editor with iPXE syntax highlighting, then Save Config to write your changes. boot.cfg is the one to know: it sets the site name, the boot timeout, live_endpoint (where live assets are pulled from - repoint this at your own mirror, see Step 11) and the upstream distribution mirrors.

Edits are saved as local overrides, so your changes survive a menu upgrade - the remote menu tree is replaced but your local files are layered back on top. Use Revert to drop an override and return to the shipped version.

The netboot.xyz menu editor showing boot.cfg open with iPXE syntax highlighting and a Save Config button

Step 11 - Mirror boot assets locally

Select Local Assets. The left pane lists every asset the 3.0.2 release knows about; the right pane lists what you have mirrored locally, which is empty on a fresh VM. Tick the assets you care about and choose Pull Selected to download them into /assets on this VM, served by nginx at /assets/.

Mirroring matters for two reasons: your machines then provision at LAN speed instead of pulling gigabytes across the internet each time, and they can boot even when your internet link is down. After mirroring, point live_endpoint in boot.cfg (Step 10) at http://<vm-ip>/assets so the menus fetch from your mirror rather than from upstream.

The netboot.xyz Local Assets view listing the remote assets available at release 3.0.2 with an empty local mirror ready to pull into

Step 12 - Point your network at the boot server

Network boot is driven by DHCP, not by this VM. Your DHCP server tells clients where to fetch a bootloader, using next-server (the TFTP server address) and a filename that depends on the client's firmware:

Client firmware Filename to serve
Legacy BIOS netboot.xyz.kpxe
Legacy BIOS, USB keyboard issues netboot.xyz-legacy.kpxe
UEFI x86_64 netboot.xyz.efi
UEFI x86_64, Secure Boot netboot.xyz-snponly.efi
UEFI arm64 netboot.xyz-arm64.efi

With ISC DHCP, the classic form is:

# /etc/dhcp/dhcpd.conf on YOUR DHCP server (not on this VM)
next-server <netbootxyz-vm-ip>;

if option arch = 00:07 or option arch = 00:09 {
    filename "netboot.xyz.efi";        # UEFI x86_64
} elsif option arch = 00:0b {
    filename "netboot.xyz-arm64.efi";  # UEFI arm64
} else {
    filename "netboot.xyz.kpxe";       # legacy BIOS
}

With dnsmasq as your DHCP server:

# on YOUR DHCP server (not on this VM)
dhcp-boot=netboot.xyz.kpxe,,<netbootxyz-vm-ip>
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-boot=tag:efi-x86_64,netboot.xyz.efi,,<netbootxyz-vm-ip>

Then set a client to boot from the network. It fetches the bootloader over TFTP from this VM, chainloads the menu, and the netboot.xyz menu appears. Choose an entry and it boots.

If you cannot change DHCP, you can still use the appliance: burn netboot.xyz.iso or write netboot.xyz.img to a USB stick from the netboot.xyz downloads, and set ${boot_domain} to this VM. That trades the "no media" benefit for not touching DHCP.

Security

  • The admin UI is the control plane for what your machines boot. Treat it accordingly. It is protected with HTTP Basic Auth using a password unique to your VM, and the application is bound to loopback so the authentication cannot be bypassed. Rotate the password whenever you need to, with sudo htpasswd -B /etc/nginx/netbootxyz.htpasswd admin.
  • Basic Auth is only as private as the transport. Port 80 is plain HTTP, so credentials cross the network base64 encoded, not encrypted. For anything beyond a trusted VNet, front the VM with TLS - your own domain behind Azure Application Gateway or a TLS terminating reverse proxy - and restrict port 80 in the NSG to the networks that need it.
  • The boot plane is anonymous by design. TFTP has no authentication in the protocol at all, and a machine that is powering on cannot present a credential. 69/udp, /menus/ and /assets/ are therefore open to anyone who can reach them, and they serve only public installer artifacts. Your NSG is the control: scope 69/udp to your client subnets and never expose it to the internet.
  • iPXE signature checking is off on a local instance. The netboot.xyz web application sets sigs_enabled false in boot.cfg at startup, because locally mirrored assets are not signed by the netboot.xyz project. This is upstream's own behaviour for local instances, not a cloudimg change, and you can see it on line 17 of boot.cfg in Step 10. It means the integrity of what you boot rests on the mirrors you point at and the network path to them - another reason to keep the boot plane on trusted networks.
  • Security updates: unattended security upgrades are enabled, so the OS keeps itself patched. Reboot when a new kernel is installed.

Maintenance

  • Upgrading the menus: the dashboard reports when a newer netboot.xyz release is available and offers Upgrade Menus to latest. Your local menu overrides are layered back on top after an upgrade, so custom entries survive.
  • What to back up: /config holds your menu overrides and endpoints.yml - back that up. /assets holds mirrored installer images, which are re-downloadable and usually not worth backing up.
  • Disk usage: mirrored assets in /assets grow quickly. Watch it with df -h /assets, and move it to a dedicated data disk if you mirror a lot.
  • Modifications: this image makes two documented changes to the upstream web application, both recorded in /opt/netbootxyz/CLOUDIMG-CHANGES.txt as the Apache License 2.0 requires: the admin app is bound to loopback so its authentication cannot be bypassed, and several dependencies are raised to versions that carry no known advisories.

Support

cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. netboot.xyz is licensed under the Apache License 2.0. This image is provided by cloudimg and is not affiliated with or endorsed by the netboot.xyz project; additional charges apply for build, maintenance and 24/7 support.