• mward4 released this 2026-05-27 13:53:20 -04:00 | 37 commits to main since this release

    OpenPXE 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 options

    That 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:

    1. The container is missing CAP_SYS_ADMIN (mount(2) returns EPERM, and
      nfs-utils transforms that into the "fstab options" message).
    2. /etc/mtab is unwritable in the container, so the option
      transformation that updates the mount table fails.
    3. 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 to server: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.nfs and then spitting out
    the unhelpful fstab message.

    proto=tcp explicit on NFSv3. UDP has been deprecated for years
    and many appliances (most UniFi/Synology setups) don't bind it. v0.4.64
    passes proto=tcp so the kernel doesn't try UDP first.

    Auto-retry with minimal options. When mount.nfs returns "failed to
    apply fstab options" or "internal option parsing error", we retry
    once with just vers=N,ro/rw — stripping proto=, 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
    optional last_hint field alongside last_error. Well-known patterns
    are translated into actionable guidance:

    Raw stderr Hint
    failed to apply fstab options / internal option parsing error Container is likely missing CAP_SYS_ADMIN — run with --cap-add=SYS_ADMIN, or use a privileged SCC on OpenShift
    Operation not permitted / Permission denied Same as above
    access denied by server Check the export's allowed-hosts list includes this OpenPXE host's IP
    no route to host / Network is unreachable Server not reachable on this network
    Connection refused NFS 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 out Firewall dropping the connection, or wrong port
    no such file or directory / does not exist Export path doesn't exist on the server (same UNAS Pro hint)
    rpc: program not registered Server doesn't speak the requested NFS version — try the other entry
    protocol not supported / invalid argument Version 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. NfsAddRequest now accepts an optional port
    that flows through into port=<n> on the mount options. Defaults to
    2049 when omitted.

    Terminal parity

    The nfs mount terminal command now prints the actionable hint on a
    follow-up hint: … 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=tcp on v3, port=N when non-default), the
      minimal-options retry path, server normalization, and every
      well-known stderr → hint translation.
    • cargo clippy --workspace --all-targets -- -D warnings clean.

    Compatibility

    • Existing nfs.json files load unchanged — port defaults to 2049
      and last_hint defaults to absent.
    • API: POST /api/nfs now 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 mount output may include a follow-up hint: line on
      failure. Existing parsers that only read the first line are
      unaffected.

    Container image

    • gitea.milesward.dev/mward4/openpxe:0.4.64
    • gitea.milesward.dev/mward4/openpxe:latest

    linux/amd64; static-musl /openpxe binary 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