-
OpenPXE v0.5.1 Pre-Release
released this
2026-05-31 02:10:37 -04:00 | 26 commits to main since this releaseOpenPXE v0.5.1
SAML SSO goes live, and the Settings/Storage tabs get a cleanup.
✨ Single sign-on (SAML 2.0) — now wired end-to-end
SSO configuration has been storage-only since v0.4.5 (you could paste your
IdP metadata, but it didn't log anyone in). v0.5.1 wires the real flow.- "Sign in with " on the login page now starts an SP-initiated SAML
login: OpenPXE builds anAuthnRequest, redirects to your IdP, and the new
/api/sso/acsendpoint consumes the signedSAMLResponse, mints an
operator session, and drops you on the dashboard. - IdP-initiated login is supported too, gated behind a new
"Allow SSO login initiated by identity provider" toggle (off by default) —
mirroring FleetDM. - Pure-Rust, no OpenSSL. Signature verification + exclusive XML
canonicalization run on the pure-Rustbergshamra
crate (RustCrypto,#![forbid(unsafe_code)]). The container stays a fully
static, OpenSSL-free musl binary — noxmlsec/libxml2C dependencies. - Security checks are the ones that matter for an SP: the signature is
verified against your IdP's pinned certificate only (an attacker can't swap
in their own key viaKeyInfo), with XML-Signature-Wrapping defenses, plus
Status / Destination / Audience / time-window validation,InResponseTo
correlation, IdP-initiated gating, and an assertion-replay guard. - Access model: any user your IdP authenticates and OpenPXE
cryptographically verifies gets an operator session. OpenPXE stays
single-tier (no user/role table); the local admin account remains a
guaranteed fallback owner regardless of SSO state. - New SSO settings: Entity ID (defaults to your public base URL) and the
IdP-initiated toggle. A newGET /api/sso/metadataendpoint serves OpenPXE's
SP metadata for easy import into your IdP.
Scope for this release: assertions must be unencrypted, AuthnRequests are
sent unsigned (your IdP must not require client signatures), and the NameID
is the user's email. Single Logout and encrypted assertions are not yet
supported.🧹 UI cleanup
- Advanced tab folded into Settings. The standalone Advanced sidebar
entry is gone; its webhook/email notification settings and the API reference
now live in a collapsible "Advanced" section at the bottom of Settings. - Storage → "Remote shares". The separate SMB shares and NFS shares
cards are merged into one Remote shares card with a protocol dropdown
(SMB / NFS) and a single, protocol-badged table. Same backends, far less
clutter — and room for Config files down the road.
✅ Quality
- 17 new SAML SP unit tests (accept the valid case; reject tampered, unsigned,
wrong-key, wrong-audience, expired, future-dated, wrong-issuer, and
non-success responses) + 6 ACS integration tests (happy path, IdP-initiated
gating, SP request correlation, assertion replay, garbage input). - Full workspace: 206 tests green, clippy clean.
🐳 Pull it
docker pull gitea.milesward.dev/mward4/openpxe:0.5.1 # or docker pull gitea.milesward.dev/mward4/openpxe:latestDownloads
- "Sign in with " on the login page now starts an SP-initiated SAML
-
released this
2026-05-29 15:12:27 -04:00 | 28 commits to main since this releaseOpenPXE v0.5.0
The v0.4.x chapter is closed — NFS works end to end — and v0.5.0
turns OpenPXE from "a PXE server" into something you can hand an L1
desktop tech: wake machines, get notified, brand the whole thing,
and check for updates, all from one container.Wake-on-LAN
Bind a MAC under Hosts, and each bound host now has a Wake
button. It sends a Wake-on-LAN magic packet to the limited broadcast
and the server's own subnet broadcast (computed from OpenPXE's
advertised IP + detected mask), so it reaches the right VLAN with zero
network config on the common "same segment" setup. No special container
capability required.Safety: WoL only fires for MACs that are actually bound — it's not an
open "spray packets at anything" endpoint.Webhook notifications (Slack / Teams / Discord / SMTP)
A new Advanced tab carries notification settings. Pick one provider,
paste the incoming-webhook URL (or fill in SMTP), hit Send test, and
you'll get a ping when:- a machine PXE-boots an image (the canonical "imaging started" moment),
- a host is woken via WoL.
All four transports are native — Slack, Discord, and Teams over their
incoming-webhook JSON shapes, email over SMTP (rustls, STARTTLS or
implicit TLS — never plaintext). Sends are best-effort and time-bounded,
so a flaky webhook can never wedge a PXE boot. The SMTP password is
stored but never returned by the API — the config screen shows
"(unchanged)" and only overwrites it if you type a new one.Advanced settings tab
New nav item under Settings. Holds the webhook config and the API
reference block (relocated from the bottom of Settings — Settings is
now just Account / SSO / Branding).Login page logo (FleetDM style)
The sign-in and first-run-setup screens now use the same brand treatment
as the dashboard: when you've uploaded a custom logo under
Settings → Branding, it spans the card header at full width and the
"OpenPXE" wordmark is dropped — your logo is the brand. No custom logo?
The bundled mark + wordmark, as before. Rendered from the public
/api/mebootstrap so there's no flash of the default.About: check for updates + licenses
- Check for updates button queries the project's Gitea releases API
and tells you whether you're current or a newer tag is out (with a
link to the release). Strictly on-demand — OpenPXE never polls in the
background, preserving the air-gapped promise. - A License card documents the dual MIT OR Apache-2.0 license
with links to both full texts, plus a note on bundled components
(iPXE GPLv2/UBDL, samba's smbclient, wimtools, the Rust crates).
Under the hood
- New
core::wolandcore::notify(config + persistence); delivery
lives inhttp-api::notify(reqwest for chat, lettre for SMTP). - New deps:
lettre(SMTP) and thejsonfeature onreqwest. Both
use rustls + ring — the static musl binary stays OpenSSL-free. - 179 tests passing (WoL magic-packet loopback, notify config
round-trip + password redaction, webhook validation, WoL-on-unbound
→ 404, version comparison).cargo clippy --workspace --all-targets -D warningsclean.
Container image
gitea.milesward.dev/mward4/openpxe:0.5.0gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxe; PNG-enabled x86_64 iPXE baked in
(from v0.4.69).Compatibility
- Additive. New state file
notify.json; existing files load unchanged. /api/megainshas_custom_logo+logo_rev(additive).- The API reference moved tabs in the UI but every endpoint is
unchanged; new endpoints:/api/hosts/:mac/wol,/api/notify,
/api/notify/test,/api/updates/check.
Downloads
-
released this
2026-05-29 03:11:35 -04:00 | 30 commits to main since this releaseOpenPXE v0.4.69
The graphical-boot-menu release. Plus the NFS fix that finally lands
NFS shares, and a FleetDM-style full-width custom logo.1. Graphical PXE boot background — built from source, no more ASCII
OpenPXE now paints a real graphical background on the PXE boot menu,
the way iVentoy does. Your uploaded logo sits across the top of the
boot screen on a clean dark field; the ISO menu renders below it. With
no logo uploaded you still get a designed background (a default OpenPXE
mark) — the old ASCII wordmark is gone entirely.How (and why it took until now): the boot-menu background needs
iPXE compiled withCONSOLE_FRAMEBUFFER+IMAGE_PNG+CONSOLE_CMD
so itsconsole --picturecommand can paint a PNG. The public iPXE
binaries omit those flags, and building iPXE ourselves had been blocked
for eight releases bycc1segfaulting under QEMU emulation on the
arm64 build host.v0.4.69 fixes the build the right way: a dedicated build stage that
runs on the native builder architecture and cross-compiles x86_64
iPXE with a real cross-compiler (x86_64-linux-gnu-gcc). No emulation,
no segfault — the iPXE build now takes ~2 minutes and is fully baked
into the image. Validated end-to-end: the PNG decoder, framebuffer
console, and pixel-buffer code are all confirmed linked into the
binary.What this means in practice:
- x86_64 UEFI clients (the overwhelming majority of modern
machines) get the full graphical background. - BIOS / i386 / arm64 clients keep the upstream binaries and fall
back to a clean text menu (no ASCII, just the menu). - Upload any PNG, JPEG, WebP, or GIF logo — OpenPXE composites it
onto a 1024×768 background and transcodes to PNG (the only format
iPXE consumes). WebP works on the input side even though iPXE itself
can't read WebP.
The same uploaded logo now drives three places: the WebUI top-left
brand mark, and the PXE boot background on machines.2. NFS shares actually mount now (NFS3ERR_ACCES fixed)
v0.4.68's privileged-port fix got past the mount denial
(MNT3ERR_ACCES); operators then hitNFS3ERR_ACCESon the directory
listing. Root cause: the NFS client defaulted toAUTH_NONE, but
essentially every NFS server (UniFi UNAS included) exportssec=sys
and rejects anonymous callers on the actual file operations.v0.4.69 presents a proper
AUTH_SYScredential (uid 0 / gid 0). On a
no_root_squashserver that's full root read; on the far more common
root_squashserver it maps to the anonymous user, which can read any
world-readable ISO share — the normal case. No new UI field: ISO
libraries are read-only shared data, and a uid knob is exactly the kind
of thing that makes a tool confusing for an L1 tech.For your UNAS Pro: server
192.168.1.51, export
/var/nfs/shared/isos, host IP allow-listed — this should now mount
and list. If you still seeNFS3ERR_ACCES, the new hint spells out
that it's now a server-side directory-permission/squash issue, not an
IP or auth-flavor one.3. FleetDM-style full-width logo (top-left)
When you upload a custom logo it now spans the sidebar header
(left-aligned, up to ~200×50, scaled to fit) and the "OpenPXE"
wordmark is hidden — the FleetDM pattern, where the uploaded logo is
the sole brand element. The bundled-default (no upload) keeps the mark- wordmark. Rendered server-side so there's no flash of the default on
load.
Quality
- 164 tests passing.
cargo clippy --workspace --all-targets -D warningsclean. - iPXE build stage validated in isolation (png/fbcon/pixbuf linkage
confirmed) before the full image build.
Container image
gitea.milesward.dev/mward4/openpxe:0.4.69gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary; PNG-enabled x86_64 iPXE
baked in.What still needs your real-world test
The PNG background is validated at the binary level (the right code is
compiled and linked) but hasn't been booted on real hardware from this
build yet. Please PXE-boot an x86_64 UEFI machine and confirm the
background paints. Once that's confirmed in the field, this chapter
closes and we move to v0.4.7.Compatibility
- Additive; no data-file format changes.
/branding/pxe-logonow always returns a PNG (was 404 with no logo).
iPXE clients are unaffected (they just get a background now).
Downloads
- x86_64 UEFI clients (the overwhelming majority of modern
-
released this
2026-05-28 21:34:41 -04:00 | 31 commits to main since this releaseOpenPXE v0.4.68
Bug-fix release addressing four issues from v0.4.67 field validation.
1. NFS mount failed with
MNT3ERR_ACCESeven with the host IP allow-listedThis is the big one. Operators added their UNAS Pro NFS export with
the host IP correctly in the allow-list and still got
connect failed: MNT3ERR_ACCES.Root cause: Linux kernel
nfsd— which UniFi UNAS, Synology, TrueNAS,
and essentially every appliance NAS run underneath — exports with the
secureoption by default.securemeans the server only accepts
mount/NFS requests whose TCP source port is privileged (< 1024). A
client connecting from an ephemeral high port is denied at mount time,
regardless of the allow-list.v0.4.67 deliberately connected from a non-privileged port on the
mistaken assumption that uid 10001 couldn't bind low ports. But the
OpenPXE binary carriesCAP_NET_BIND_SERVICE(granted viasetcapso
it can bind the DHCP/TFTP/HTTP low ports as non-root), and that
capability also lets it bind a privileged source port for an
outbound connection.Fix: OpenPXE now connects from a privileged source port first (what
every appliance NAS with defaultsecureexports needs), and falls
back to a non-privileged port forinsecureexports or environments
without the capability. Each attempt has its own connect timeout; a
no-answer timeout on the first attempt skips the fallback so an
unreachable server doesn't double your wait.For your UniFi UNAS Pro: with v0.4.68, server
192.168.1.51+
export/var/nfs/shared/isos(the full path the UNAS mount command
shows — not just/isos) should now mount. No server-side change
needed since your export uses the defaultsecureoption and OpenPXE
now speaks it correctly.If a mount still fails, the error banner now distinguishes
MNT3ERR_ACCESand spells out both checks: host IP in the allow-list,
and addinginsecureto the export if the server refuses privileged
ports for some reason.2. Custom logo didn't update the top-left brand mark
The WebUI brand mark and favicon were cache-busted only by the OpenPXE
version (?v=0.4.67), which doesn't change when you upload a logo — so
the browser kept serving the cached default mark.Fix: the branding store now carries a revision counter that bumps on
every logo upload/clear and is woven into the brand-mark URL as a second
cache-bust token. Upload a logo, the page reloads, the new mark appears
immediately. Remove it, the default comes back. No hard refresh needed.About PXE-booting machines: this fixes the WebUI top-left mark. The
PXE boot menu still shows the ASCII "OpenPXE" wordmark, not your
uploaded image — painting a PNG in the iPXE menu requires an
IMAGE_PNG-enabled iPXE build, which the stock boot.ipxe.org binaries
don't include. The server-side compositor that prepares your logo for
that paint (/branding/pxe-logo) is wired and ready; it goes live the
moment the iPXE rebuild lands (queued for native x86_64 hardware). Until
then, the boot menu wordmark is text-only by design.3. Disk-space card on the Dashboard
The disk-space card from the Storage tab now also appears on the
Dashboard, directly under the stat strip, so capacity is visible from
the landing page. Same coloured bar, same 80%/95% warnings.4. NFS "Add share" button spacing
The NFS shares form has a single row (vs the SMB form's two), so the Add
share button sat flush against the input fields. Added top spacing to
match the SMB card.Quality
- 162 tests passing (+2: logo-revision bump, MNT3ERR_ACCES hint).
cargo clippy --workspace --all-targets -- -D warningsclean.
Container image
gitea.milesward.dev/mward4/openpxe:0.4.68gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary.Compatibility
- Additive. No data-file format changes;
branding.jsongains arev
field that defaults to 0 on existing installs. - API and route surfaces unchanged.
Downloads
-
released this
2026-05-28 12:56:52 -04:00 | 32 commits to main since this releaseOpenPXE v0.4.67
NFS is back. Done right this time.
TL;DR
OpenPXE now supports both SMB and NFSv3 for remote ISO libraries,
side-by-side. Pick whichever your NAS exposes. Both work in any
container — no kernel modules, noCAP_SYS_ADMIN, no--privileged.
Both are baked into the single OpenPXE image with zero additional
dependencies for the operator to install.Why NFS, again
The user asked for it after validating SMB works. Most NAS appliances
(UniFi UNAS Pro, Synology, TrueNAS, OpenMediaVault) expose both
protocols. Some shops prefer NFS for performance, some prefer SMB for
the simpler auth model. v0.4.67 lets operators not have to choose.What's different from the v0.4.64 NFS path
v0.4.64 shelled out to
mount(8)and asked the kernel to attach the
remote share. That required NFS client modules loaded on the host
kernel. Containers on hosts without those modules — Unraid being the
dominant case — failed regardless of container capabilities.v0.4.67 doesn't touch the kernel. The new
NfsShareManageruses the
nfs3_clientRust crate to
speak NFSv3 over a plain TCP socket entirely in userspace. The crate
runs in-process inside the openpxe binary — no subprocess, no
mount.nfs, no PATH lookups. The kernel sees plain TCP traffic.Side-by-side with SMB
Storage tab now has two cards stacked: SMB shares (v0.4.65,
unchanged) and NFS shares (v0.4.67). Both forms use the same
{error, stderr, hint}JSON error shape so the UI's "connect
failed" banner renders identically for both protocols. Operators
can run any mix of SMB and NFS shares simultaneously; the Available
images table shows their ISOs intermixed with badges (smb:<id>/
nfs:<id>/local).NFS has one architectural advantage over the SMB userspace path
Range requests work. NFSv3 READ3 takes an explicit offset, so
the HTTP ISO download handler can seek into the middle of a 5 GB
ISO without reading what comes before. SMB-sourced ISOs still
return 416 for ranges becausesmbclient -c 'get file -'is a
sequential stream — that's a smbclient CLI limitation, not a
protocol one. For PXE workloads (iPXE chain, casper sanboot,
wimboot) it doesn't usually matter; for casper installers with
iso-url=-style random-access reads, it does.What's the same
IsoStore::iso_path_forreturnsNonefor both SMB and NFS
sources; the HTTP handler dispatches to the right share manager.- Hint translation is parallel:
NT_STATUS_*for SMB,NFS3ERR_*
for NFS, mapped to actionable operator-facing guidance. - Persistence:
<work_dir>/smb_shares.jsonand
<work_dir>/nfs_shares.jsonside by side.
NFS auth (or lack thereof)
NFSv3 uses AUTH_SYS — the client tells the server "I'm uid X" and
the server decides whether to trust that. There's nothing for the UI
to ask for, because access control is on the server side by client
IP. The form is just two fields: server and export path.If your server demands privileged-port clients (sub-1024 source
port), addinsecureto the export in/etc/exports. OpenPXE runs
as uid 10001 and can't bind sub-1024 source ports — the hint
translation calls this out specifically when a MOUNT3 fails.Terminal
The
nfscommand is back, this time as in-process:nfs list nfs add 10.0.0.5:/srv/isos nfs add 10.0.0.5:/srv/isos 2049 nfs scan <id> nfs remove <id>The v0.4.64
nfscommand (kernel mount) and the v0.4.67nfs
command (in-process via nfs3_client) share the name but not the
mechanism. v0.4.64 was deleted in v0.4.65; this is the new
implementation.Hints for the common NFSv3 errors
Wire error UI hint NFS3ERR_ACCESThe server rejected this client. Most likely your export is restricted by client IP — add this OpenPXE host (or its subnet) to the export's allowed-clients list on the server. NFS3ERR_NOENT/NFS3ERR_NOTDIRThe export path doesn't exist on the server, or it isn't a directory. UniFi UNAS Pro exposes shares under /var/nfs/shared/<name>.NFS3ERR_STALE/NFS3ERR_BADHANDLEThe server's view of the share changed under us. Re-scan; if that doesn't help, remove and re-add the share. connect timed outVerify IP, port (default 2049), and firewall. connection refusedMake sure nfsdis running and (for v3) portmap on port 111 is reachable.mount denied/etc/exportsmay needinsecure— OpenPXE runs as uid 10001 and uses non-privileged source ports.Quality
- 160 tests passing (was 150 in v0.4.66, +10).
cargo clippy --workspace --all-targets -- -D warningsclean.- nfs3_client is at 0.9.0 (MIT/Unlicense, rust-version 1.88 — we're
on 1.95).
Image
gitea.milesward.dev/mward4/openpxe:0.4.67gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary. Image grows by the size
of the linked nfs3_client + nfs3_types crates (small, <500 KiB on
disk).What about v0.5
v0.5 ships once a few more operators have validated SMB and NFS in
production. Likely scope: an Audit/Logs tab, the iVentoy-style
themed PXE menu (the rebuilt iPXE withIMAGE_PNGfrom native
x86_64 hardware that's been queued since v0.4.61), and whatever
operator-reported issues land between now and then. This v0.4.67 is
the last feature drop before that consolidation.Compatibility
- API is additive — every v0.4.66 client keeps working.
- No data file format changes;
auth.json,branding.json,
sso.json,hosts.json,meta.json,smb_shares.jsonall load
identically. - The Prometheus
nfs_activegauge now sums SMB + NFS reachable
shares (it had been re-purposed to track SMB only in v0.4.65;
v0.4.67 brings it back to "external storage shares reachable"
which matches the original v0.4.4 intent).
Downloads
-
v0.4.66 — Ship smbclient in the runtime image Pre-Release
released this
2026-05-28 11:58:56 -04:00 | 33 commits to main since this releaseOpenPXE v0.4.66
Hotfix for v0.4.65. The new SMB shares manager shelled out to
smbclient— which wasn't actually in the v0.4.65 image. Every
"Add share" surfaced:Mount failed: could not exec smbclient: No such file or directory (os error 2)
What happened
In Debian 12, the
sambapackage ships the server (smbd,
nmbd) only. ThesmbclientCLI lives in a separatesmbclient
package — they're siblings, not parent/child. v0.4.65's Dockerfile
only installedsamba, so the new userspace SMB consumer had nothing
to exec.Fix
- Runtime image's
apt-get installline now includessmbclient.
The image grows by less than 1 MB (still ~98 MB total). - Removed
nfs-commonfrom the runtime image — v0.4.65 stopped
usingmount.nfsso the helpers were dead weight.
Defensive improvements
So that this exact failure mode never bites again (e.g. if anyone
builds a stripped-down custom image), bothlist_isosand
stream_isonow detect ENOENT on spawn and surface:Mount failed: could not exec smbclient: No such file or directory (os error 2)
smbclient isn't installed in this container. Pull the official OpenPXE
image v0.4.66 or newer — the stock image ships smbclient. If you're
running a custom build, add the Debiansmbclientpackage to your
runtime stage.The hint translation table picks up the missing-binary pattern
verbatim, so the UI banner shows the remediation alongside the raw
error.How to upgrade
Pull
:0.4.66(or:latest). Existing SMB share configurations in
<work_dir>/smb_shares.jsonare loaded as-is and re-scanned on
startup — once the binary is present, the next scan succeeds and
your ISOs surface in Available images.Quality
- 150 tests passing (+1 from v0.4.65 — the new missing-binary
hint translation has its own test). cargo clippy --workspace --all-targets -- -D warningsclean.
Container image
gitea.milesward.dev/mward4/openpxe:0.4.66gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary unchanged from v0.4.65.Compatibility
- No source-side changes that affect persisted state.
- API and UI surfaces unchanged.
- The fix is image-only — code-wise this release just hardens the
error path for an environment that v0.4.66's image makes
unreachable.
Downloads
- Runtime image's
-
released this
2026-05-28 11:19:53 -04:00 | 34 commits to main since this releaseOpenPXE v0.4.65
NFS is gone. SMB shares are in.
Why
v0.4.64 added pre-flight probes, retry-with-minimal-options, and a
catalog of actionable hints for kernel-mount NFS. None of that helps
when the host kernel doesn't have thenfsclient modules loaded —
mount(2) inside the container returns EOPNOTSUPP / EINVAL and there's
nothing container-side configuration can do about it. Unraid's base
kernel ships without those modules. CIFS (kernel-mount SMB) has the
identical problem.The right fix is to not depend on kernel mounts at all. Samba's
smbclientCLI speaks the SMB protocol entirely in userspace over a
plain TCP socket — no kernel modules, noCAP_SYS_ADMIN, no
--privileged. Bootimus uses this approach. iVentoy / FOG / MAAS /
Cobbler all rely on the host to mount network storage; OpenPXE
v0.4.65 ships its own SMB consumer so operators don't have to.What got removed
- The whole
crates/iso-store/src/nfs.rsmodule —NfsManager,
NfsMount,NfsAddRequest,NfsVersion, mount-options retry, TCP
probe, stderr-to-hint translation. IsoSource::Nfsvariant on the ISO source enum.IsoStore::set_nfs_rootand thenfs_rootfield.- Every
/api/nfs/*route. - The
nfsterminal command. - The Storage tab's "NFS shares" card.
If you had NFS mounts persisted in
<work_dir>/nfs.jsonthey're
ignored at startup; OpenPXE no longer reads that file.What got added
crates/iso-store/src/smb_share.rs—SmbShareManagerthat drives
smbclientas a subprocess. Indexes a share via
smbclient //server/share -A creds -c "ls *.iso". Streams files
viasmbclient //server/share -A creds -c "get file -"piped
straight into HTTP response bodies — no local cache, no double
disk usage.IsoSource::Smb { share_id, relative_path }variant./api/smb-sharesAPI (list / add / delete / scan).shareterminal command:share list share add //10.0.0.5/isos guest share add //nas.lan/isos myuser:mypassword share scan <id> share remove <id>- Storage tab: a new SMB shares card with a two-column form for
server + share name, three-column form for the Guest checkbox /
username / password. Username & password auto-disable when Guest
is checked. - TCP pre-flight probe (4s timeout) to
server:445before shelling
out to smbclient. Catches wrong-IP / firewall cases with a clean
error instead of waiting for smbclient to time out. - Operator-friendly hints for the common NT_STATUS codes:
LOGON_FAILURE→ check credentials;BAD_NETWORK_NAME→ check
share name (e.g.//nas/isos, not//nas/shared/isos);
ACCESS_DENIED→ check share-side permissions;CONNECTION REFUSED
/TIMEOUT→ verify smbd is running + firewall.
Credentials
- Username and password live in a 0600-permissioned credentials file
under<work_dir>/smb_creds/<share-id>.cred. Passing them on the
command line would leak them throughpsand the container audit
log. - Guest mode (
-Nto smbclient) is the default and most NAS
appliances exposing ISO libraries support it.
How to use it on Unraid (the original failure case)
- Pull
:0.4.65(or:latest). - Restart the container with whatever capabilities you had before —
none of the NFS workarounds matter anymore. Plaindocker run
with no special caps works. - In your UniFi UNAS Pro / Synology / TrueNAS / whatever, make sure
SMB is enabled on the share holding your ISOs. (UniFi UNAS Pro:
Drive → Settings → Services → SMB. Most setups have it on by
default.) - In OpenPXE's Storage tab, the SMB shares card now has fields:
- SMB server:
192.168.1.51 - Share name:
isos(just the share name, not a path) - Guest: checked if the share allows anonymous read; uncheck
to enter a username and password.
- SMB server:
- Click Add share. OpenPXE lists the share's ISOs and surfaces
them in the Available images table within seconds.
Known limitation
Range requests on SMB-sourced ISOs return 416.
smbclient -c 'get file -'is a sequential whole-file stream — there's no native seek
in the CLI. PXE workloads in practice (iPXE chain loading, casper
sanboot, wimboot) all do sequential whole-file reads, so this works
for ISO delivery. If a workload comes up that genuinely needs random
access into SMB-sourced ISOs, a follow-up release can swap to
libsmbclient-based seek without changing the API or the UI.ISO introspection (auto-detecting the distro family, finding kernel
- initrd paths inside the ISO9660 PVD) is also currently no-op for
SMB-sourced ISOs since it would require seeking into the file over
the network. Operators get genericsanbootboot entries that work
for any bootable ISO. A boundedsmbclient getof the first 64 KiB
would be enough to drive real detection; punted to a follow-up.
About v0.4.65 (the bind-mount one)
Earlier today I published a v0.4.65 release that proposed a
"register a bind-mounted host directory" workaround instead of an
in-container SMB client. The operator correctly pointed out that's
not the right architecture — it makes OpenPXE depend on operator-
managed host plumbing instead of being self-contained. That release
has been deleted from Gitea (release, tag, and container image all
removed), and the commit that introduced it has been reverted on
main. This v0.4.65 is the SMB swap; that's what the version
number now means.Tests
149 passing,
cargo clippy --workspace --all-targets -- -D warnings
clean. New tests cover:- Parser: ISO listing with skipped dot/dotdot, filenames containing
spaces, non-ISO files correctly skipped. - Server normalization (
smb://,cifs://,\\,//all stripped). - Hint translation for
LOGON_FAILURE,BAD_NETWORK_NAME, unknown. - HTTP integration: missing server, missing username when not Guest,
paths in share name all rejected with the right hint.
Container image
gitea.milesward.dev/mward4/openpxe:0.4.65gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary. Image size ~98 MB (the
samba package was already shipped for the outbound smbd serving
Windows install media;smbclientcomes with it for free).Compatibility
nfs.jsonis no longer read. If you had NFS mounts configured,
re-create them as SMB shares in the new Storage tab.- The Prometheus gauge previously named "nfs_active" still exists
but now tracks reachable SMB shares (same semantic — number of
external storage sources currently working). - The
/api/statusJSON shape gainssmb_share_countand
smb_share_reachable; the oldnfs_count/nfs_activefields
are gone. Any external monitoring keyed off the old names needs
updating.
Downloads
- The whole
-
released this
2026-05-27 13:53:20 -04:00 | 37 commits to main since this releaseOpenPXE v0.4.64
Patch release that turns the NFS mount failure path from a dead-end
into something operators can actually act on.The bug
Configuring an NFS share from the Storage tab against an NFSv3 (or v4.1)
endpoint surfaced this in the UI banner:Mount failed: invalid input: mount exit 32: mount.nfs: failed to
apply fstab optionsThat message is from
nfs_options2string()in nfs-utils 2.6.x, and is
deeply misleading — it has nothing to do with/etc/fstab. The
dominant root causes are:- The container is missing
CAP_SYS_ADMIN(mount(2) returns EPERM, and
nfs-utils transforms that into the "fstab options" message). /etc/mtabis unwritable in the container, so the option
transformation that updates the mount table fails.- An auxiliary mount option (
timeo=,retrans=) tripped a known
transform edge case in nfs-utils 2.6.x.
None of which the operator could tell from the original error.
What changed
Pre-flight probe. Before shelling out to
mount(8)we now try a TCP
connect toserver:2049(or the configured port) with a 4-second
timeout. A wrong IP or a firewall now produces:Mount failed: cannot reach NFS port: 192.168.1.51:2049: timed out
after 4s
no TCP answer from 192.168.1.51:2049 within 4s — check the IP and
any firewall in between…instead of getting stuck waiting on
mount.nfsand then spitting out
the unhelpful fstab message.proto=tcpexplicit on NFSv3. UDP has been deprecated for years
and many appliances (most UniFi/Synology setups) don't bind it. v0.4.64
passesproto=tcpso the kernel doesn't try UDP first.Auto-retry with minimal options. When
mount.nfsreturns "failed to
apply fstab options" or "internal option parsing error", we retry
once with justvers=N,ro/rw— strippingproto=,nolock,soft,
timeo=,retrans=. That bypasses the nfs-utils option-transform
quirk; if it still fails, we get a real kernel error we can translate.Stderr → hint translation. Each persisted mount now carries an
optionallast_hintfield alongsidelast_error. Well-known patterns
are translated into actionable guidance:Raw stderr Hint failed to apply fstab options/internal option parsing errorContainer is likely missing CAP_SYS_ADMIN — run with --cap-add=SYS_ADMIN, or use a privileged SCC on OpenShiftOperation not permitted/Permission deniedSame as above access denied by serverCheck the export's allowed-hosts list includes this OpenPXE host's IP no route to host/Network is unreachableServer not reachable on this network Connection refusedNFS isn't listening here — verify the export path (UniFi UNAS Pro exports under /var/nfs/shared/<name>, not the share name on its own)Connection timed outFirewall dropping the connection, or wrong port no such file or directory/does not existExport path doesn't exist on the server (same UNAS Pro hint) rpc: program not registeredServer doesn't speak the requested NFS version — try the other entry protocol not supported/invalid argumentVersion mismatch — try the other entry The structured
{error, stderr, hint}is returned over the API and
rendered in the UI as two lines: bold raw error, dimmer actionable
hint underneath.Server normalization.
http://,https://,nfs://schemes and
trailing slashes pasted into the server field are now stripped. A
port suffix on the server (nas.lan:2049) is still preserved.Optional port field.
NfsAddRequestnow accepts an optionalport
that flows through intoport=<n>on the mount options. Defaults to
2049 when omitted.Terminal parity
The
nfs mountterminal command now prints the actionable hint on a
follow-uphint: …line so SSH/terminal users see the same diagnostic
information the UI does.Quality
- 150 tests passing (up from 142). 8 new tests cover the option
string (incl.proto=tcpon v3,port=Nwhen non-default), the
minimal-options retry path, server normalization, and every
well-known stderr → hint translation. cargo clippy --workspace --all-targets -- -D warningsclean.
Compatibility
- Existing
nfs.jsonfiles load unchanged —portdefaults to 2049
andlast_hintdefaults to absent. - API:
POST /api/nfsnow returns a JSON body on failure instead of
plain text. The body shape is{error, stderr, hint}. Clients that
previously read the body as text still get readable JSON. - Terminal:
nfs mountoutput may include a follow-uphint:line on
failure. Existing parsers that only read the first line are
unaffected.
Container image
gitea.milesward.dev/mward4/openpxe:0.4.64gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary unchanged. Image size ~98 MB.One thing v0.4.64 does not fix
If your container genuinely doesn't have
CAP_SYS_ADMIN, NFS mounts
will still fail — that's a Linux mount(2) requirement, not something
OpenPXE can work around. What v0.4.64 does fix is making it obvious
that's the problem. The Storage tab now tells you so in plain English.Downloads
- The container is missing
-
released this
2026-05-26 02:32:38 -04:00 | 38 commits to main since this releaseOpenPXE v0.4.63
Three UI fixes operators flagged on v0.4.62, plus the queued PXE-theme
research note for the next release. No backend behaviour changes.Settings card alignment
The SSO header now sits in the same 4-column grid as the Administrator
account card above it — display name / IdP logo URL / Metadata source /
IdP metadata URL, column-for-column aligned with Current password /
New username / New password / Confirm. Previously the SSO row used a
3-column grid that didn't line up with the 4-column row above and the
metadata URL hung underneath in its own row.When the operator picks "Metadata XML" the URL column collapses out and
the multi-line<EntityDescriptor>textarea drops onto its own
full-width row below — there's no way to fit a 6-row textarea into a
single grid cell without making everything around it look stretched.Enable-single-sign-on checkbox now actually paints
<meta name="color-scheme" content="dark light">inindex.htmlwas
telling the browser "default to dark form chrome on a tie" — which
forced the SSO enable-checkbox to render as an opaque black square in
light mode regardless of theaccent-colorCSS hint. (Specs are
specs:accent-coloronly repaints the check mark, not the
container.)Two things land in v0.4.63:
- CSS
color-schemetracksdata-themedirectly —darkwhen the
OpenPXE theme is dark,lightwhen it's light. Overrides the meta
so the rest of the native chrome (date pickers, scroll bars) also
follows the active palette. - The checkbox itself is now custom-styled — 16x16 rounded square,
accent fill + tick on:checked, hover ring, focus glow. Chrome,
Firefox and Safari now render the control identically across both
palettes; we no longer depend on whichever WebKit happens to honour
accent-color.
Dropdown affordance restored
With
-webkit-appearance: noneon inputs and selects (used to bring
password/text inputs to a consistent baseline), the native dropdown
arrow disappeared on<select>. That's why "Metadata source" looked
squished — there was no visible cue the field was a picker. v0.4.63
paints a chevron back in via a data-URI SVG (themed light/dark) and
reserves 30 px of right-padding for it.Spacing under the action buttons
Update credentialsandSave SSO settingsnow sit with explicit
margin above them so they no longer butt against the input row above.Queued: iVentoy-style PXE menu theming
docs/queued/ipxe-pxe-menu-theme-research.mdcaptures the design for
the next release:- iVentoy uses iPXE's
console --picturewith baked-in per-resolution
PNGs (1024×768 default, plus 1280×1024 and 1920×1080 selectable in
its Configuration tab). No EDID auto-detect — daemon-side resolution
pick. - The same primitive is already wired in
crates/http-api/src/ipxe_script.rs. - Plan: extend the existing
pxe_logo.rscompositor to emit
per-resolution framed PNGs with title bar + footer painted into the
background, thenconsole --left/--right/--top/--bottominsets the
iPXE menu into the framed window. This lands once the
IMAGE_PNG-enabled iPXE rebuild ships on native x86_64 hardware.
Container image
gitea.milesward.dev/mward4/openpxe:0.4.63gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary unchanged.Quality
- 142 tests passing (unchanged from v0.4.62).
cargo clippy --workspace --all-targets -- -D warningsclean.
Compatibility
- Existing deployments upgrade in place. No data-file format changes;
auth.json,branding.json,sso.json,hosts.json,meta.json
all read identically. - Browser cache-bust from v0.4.62 carries the new asset URLs into
every session without a hard refresh.
Downloads
- CSS
-
released this
2026-05-26 00:53:30 -04:00 | 39 commits to main since this releaseOpenPXE v0.4.62
The buildable companion to v0.4.61. Same cache fix and PXE-logo
compositor; the iPXE-rebuild-with-PNG-support stage from v0.4.61 has
been walked back and queued for a follow-up release once it can be
validated on native x86_64 hardware.Why v0.4.62 exists
v0.4.61 ships in source with an
ipxe-buildDockerfile stage that
compiles iPXE from source withIMAGE_PNGenabled. That stage hits
intermittentcc1: internal compiler error: Segmentation faultwhen
cross-emulating x86_64 gcc under QEMU on arm64 build hosts. No
v0.4.61 image was ever published as a result.v0.4.62 keeps everything else from v0.4.61 (the actual fix the
operator reported, plus the compositor code) and rolls the iPXE
build stage back to the boot.ipxe.org fetch path. The compositor at
/branding/pxe-logois wired and ready — when the iPXE-rebuild stage
lands in a follow-up release (on native x86_64 hardware), no other
code change is needed. The operator's logo will start painting in the
PXE menu the moment the underlying iPXE binaries grow PNG support.The actual fix: v0.4.6 UI changes land in your browser
Operators who pulled v0.4.6 didn't see the v0.4.6 UI changes — the
top-right user menu, the IdP logo URL field, the realigned account
form. The version chip in the footer correctly read "v0.4.6" because
it's dynamic, butapp.jsandapp.csslive at fixed URLs that
browsers happily reused from before the upgrade.Fixed in v0.4.62:
index.htmlnow appends?v=<openpxe-version>to every asset URL
(app.css, app.js, logo.svg). Every release ships brand-new asset
URLs.Cache-Control: no-cache, must-revalidateon every asset handler.
index.html itself is no-cache so the browser learns about the new
?v=…value on the next page load.
Net effect: v0.4.62 → next release upgrades, and any prior upgrade
once you're on v0.4.62, will land in the browser on the next page
load. No hard refresh required.PXE-logo compositor (ready, waiting for PNG-enabled iPXE)
Even though the operator's logo can't paint in the PXE menu yet
(boot.ipxe.org's binaries omitIMAGE_PNG), the server-side
compositor that prepares the logo for that paint is in place:- New
openpxe-iso-store::pxe_logo::compose_pxe_logodecodes any
raster the operator uploads (PNG / JPEG / WebP / GIF), downscales
to fit a 600×200 bounding box, and pastes it horizontally centered
with a 64-pixel top margin onto a transparent 1024×768 canvas.
Returns PNG bytes. GET /branding/pxe-logoreturns that composed PNG. SVG uploads
still 404 here (iPXE can't rasterize SVG); the WebUI's top-left
mark continues to read directly from the SVG upload.
The follow-up release will swap the boot.ipxe.org fetch for a
make-from-source stage withIMAGE_PNGenabled, at which point
this endpoint goes live without any code change.What stayed from v0.4.6
The ASCII OpenPXE wordmark in
render_menuis back for v0.4.62. It
paints on every iPXE build regardless of PNG support — it's the
correct fallback for the binaries we currently ship. When the real
image paints, it'll be retired in the same release.Container image
gitea.milesward.dev/mward4/openpxe:0.4.62gitea.milesward.dev/mward4/openpxe:latest
linux/amd64; static-musl
/openpxebinary unchanged. Image size
~98 MB.Quality
- 142 tests passing (unchanged from v0.4.61): the compositor
tests still run; the menu test asserts the ASCII banner is present. cargo clippy --workspace --all-targetsclean.
Compatibility
- Existing deployments upgrade in place. No data-file format
changes; all ofauth.json,branding.json,sso.json,
hosts.json,meta.jsoncontinue to read identically. - The v0.4.61 git tag stays where it is — broken Dockerfile,
reference-only. Pull v0.4.62 (or:latest) for a working image.
Downloads