feat(extensions): add netboot extension for TFTP+NFS diskless boot#9656
feat(extensions): add netboot extension for TFTP+NFS diskless boot#9656iav wants to merge 6 commits intoarmbian:mainfrom
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds a netboot extension to stage TFTP/NFS boot artifacts and support NFS-root images, updates build framework to recognize Changes
Sequence Diagram(s)sequenceDiagram
participant Builder as Armbian Builder
participant Ext as Netboot Extension
participant KernelCfg as Kernel Config
participant ImgGen as Image Generator
participant TFTP as TFTP Staging
participant Hook as netboot_artifacts_ready Hook
Builder->>Ext: trigger (ROOTFS_TYPE=nfs-root)
Ext->>Ext: normalize & validate vars (NETBOOT_SERVER, MAC, paths)
Ext->>KernelCfg: enable NFS client/kernel options
Ext->>ImgGen: customize image (disable resize, skip firstlogin)
ImgGen->>ImgGen: build rootfs
Ext->>ImgGen: handle ROOTFS_COMPRESSION (gzip/zstd/none) / rsync export
Ext->>TFTP: collect kernel (Image/zImage), DTBs, optional uInitrd
Ext->>TFTP: generate PXELINUX entries (per-MAC or default)
Ext->>Hook: invoke netboot_artifacts_ready with artifact context
sequenceDiagram
participant DHCP as DHCP Server
participant Client as Target Device
participant UBoot as U-Boot
participant TFTP as TFTP Server
participant NFS as NFS Server
Client->>DHCP: request IP + boot options
DHCP->>Client: reply (serverip, bootfile)
Client->>UBoot: PXE/disk boot flow
UBoot->>TFTP: fetch pxelinux.cfg / kernel / dtb / uInitrd
TFTP->>UBoot: deliver artifacts
UBoot->>NFS: mount nfsroot (from cmdline)
NFS->>Client: provide root filesystem
Client->>Client: boot kernel with NFS root
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extensions/netboot/netboot.sh`:
- Around line 38-42: The function
extension_prepare_config__netboot_force_nfs_rootfs sets ROOTFS_TYPE too late (it
runs during do_extra_configuration) so NFS-specific branches in
do_main_configuration never see it; move the ROOTFS_TYPE assignment earlier by
ensuring extension_prepare_config__netboot_force_nfs_rootfs (or its logic) runs
before do_main_configuration—either call that function in the init/startup hook
that executes prior to main configuration (e.g., extension_init or top-level
script before do_main_configuration) or export/set declare -g ROOTFS_TYPE="nfs"
at script initialization so the global variable is in effect for
do_main_configuration and the NFS setup in
lib/functions/configuration/main-config.sh executes.
- Around line 147-149: The PXE staging copies ${MOUNT}/boot/uInitrd but never
references it in the generated extlinux.cfg, so U-Boot won't load the initramfs;
modify the netboot stanza generation to set an initrd_line variable when the
file exists (mirror how fdt_line is created — e.g. check for
"${MOUNT}/boot/uInitrd", set initrd_line="INITRD ${tftp_prefix_dir}/uInitrd")
and then include ${initrd_line} in the emitted PXE stanza alongside fdt_line so
the INITRD directive is present for U-Boot.
In `@extensions/netboot/README.md`:
- Around line 137-155: The README has multiple unlabeled fenced code blocks
(e.g., the directory tree block showing "/srv/netboot/" and other blocks around
lines 159-164, 171-175, 465-489, 544-547); update each triple-backtick fence to
include an appropriate info string (for example use sh, ini, or text as
appropriate) so markdownlint stops flagging them—locate the fences by searching
for the blocks that display the directory tree and configuration snippets and
add the matching language tag to each opening ``` line.
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 82-93: The rsync into ROOTFS_EXPORT_DIR can leave stale files
behind when the export tree is reused; update the rsync invocation in
rootfs-to-image.sh (the run_host_command_logged rsync call) to include --delete
(and optionally --delete-excluded) so files removed from the source are removed
from "${ROOTFS_EXPORT_DIR}" as well; add the flag either into $rsync_ea or
directly in the rsync command invocation to ensure exported NFS root mirrors the
built image.
- Around line 69-76: The archive creation pipeline (tar … | pv … |
${archive_filter} > "${ROOTFS_ARCHIVE_PATH}") must be guarded with pipefail so
failures in tar or pv aren't masked by a later stage; wrap that pipeline in a
shell context with set -o pipefail (or enable set -o pipefail locally before
running the pipeline) so a nonzero exit from tar or pv will cause the script to
fail and propagate the error from creating ROOTFS_ARCHIVE_PATH; update the block
around display_alert/ tar / pv / ${archive_filter} to ensure pipefail is active
for that pipeline.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fca3bef5-36a8-4aaa-81dd-645853e7579e
📒 Files selected for processing (5)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/rootfs-to-image.sh
`nfs-root` is a new rootfs type distinct from the existing `nfs` hybrid mode. Selecting it wires the `netboot` extension from the core `ROOTFS_TYPE` dispatch in `do_main_configuration`, so callers no longer need a separate `ENABLE_EXTENSIONS=netboot`. The legacy `nfs` branch (kernel+DTB on local boot partition, `/` over NFS) is untouched — both paths coexist until the hybrid mode's future is decided. Core plumbing mirrors the `nfs` branch for all paths where local root storage would be meaningless: partition layout skip (`prepare_partitions`), archive/export gate and version suffix (`rootfs-to-image.sh`), and the host-side filesystem compatibility check in `main-config.sh`. Extension hooks now key on `ROOTFS_TYPE=nfs-root` instead of guessing from `nfs`, removing the `force ROOTFS_TYPE=nfs` shim that ran too late relative to `do_main_configuration`. Also folded in from CodeRabbit review on PR armbian#9656: - pipefail around tar|pv|compressor so truncated archives no longer slip through on an intermediate stage failure - `rsync --delete` on `ROOTFS_EXPORT_DIR` so stale files from a previous build don't linger in the NFS export tree - explicit `INITRD` directive in extlinux when `uInitrd` is staged; U-Boot only loads an initramfs when the stanza names it - README updated to document `ROOTFS_TYPE=nfs-root` as the single switch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Very nice. With recent u-boot (2026.04, thanks to Kwiboo), one can enable LWIP networking stack for better TFTP performance (loading kernel and initramfs) -- there's also some TFTP Window Size stuff that can be tuned. U-boot with LWIP + u-boot's |
|
Thanks for the pointer! Let's get this merged first — iterating on something that works is the easy part. |
a76e4b2 to
fec99ac
Compare
fec99ac to
14e6f77
Compare
…YPE=nfs ROOTFS_TYPE=nfs produces a rootfs tarball via `tar cp ... | gzip` in rootfs-to-image.sh and never mounts NFS on the build host. The NFS client runs on the target's kernel at boot time, so host-side NFS filesystem support is irrelevant. Additionally, the check runs inside the Armbian docker launcher where /lib/modules is empty, causing `modprobe nfs` to fail even when the host kernel has nfs.ko available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous template was hardcoded for sunxi arm32 — `zImage`/`bootz`,
`console=tty1 console=ttyS0,115200`, `disp.screen0_output_mode=...`,
`ext4load/fatload mmc 0`. It did not work on arm64 boards (e.g.
rockchip64/helios64) and baked in a 115200 baud that breaks non-standard
UART speeds (helios64 runs at 1500000).
Rewrites the U-Boot hush script to:
- Prefer `Image` + `booti` (arm64), fall back to `zImage` + `bootz` (arm32).
- Load via `${devtype} ${devnum}:${distro_bootpart}` from U-Boot's distro
bootflow scanner instead of hardcoded `mmc 0`.
- Drop the `console=` overrides — let the kernel resolve the console from
DTB `/chosen/stdout-path`, keeping `earlycon` for early output.
- Drop sunxi-specific `disp.screen0_output_mode` and legacy `.next`/
`script.bin` probe paths.
For the full TFTP+NFS netboot flow see the new `netboot` extension; this
template remains the hybrid path where the kernel + DTB live on a local
boot partition and only the rootfs is NFS-mounted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
51c1bdc to
252da73
Compare
…TFS_COMPRESSION
Split the rootfs pipeline so it can produce either (or both) of:
* a tarball — via ROOTFS_COMPRESSION=gzip (default, .tar.gz) / zstd
(.tar.zst); matches existing behaviour, with zstd now available for
faster creation and decompression on modern hardware.
* an exported tree — via ROOTFS_EXPORT_DIR=<abs path>, which rsyncs
the built rootfs to a host-side directory (e.g. an NFS export)
without an intermediate archive.
Set ROOTFS_COMPRESSION=none when only the exported tree is wanted;
this skips archive creation entirely. If both ROOTFS_COMPRESSION=none
and -z ROOTFS_EXPORT_DIR are set the run is aborted — otherwise the
rootfs stage would produce nothing.
This is infrastructure; no existing callers need to change. The
netboot extension (added in a following commit) consumes
ROOTFS_EXPORT_DIR to populate the NFS export tree.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
252da73 to
a10feb4
Compare
Hooks:
- extension_prepare_config: validate variables, compute defaults for
NETBOOT_TFTP_PREFIX / NETBOOT_NFS_PATH (shared by
LINUXFAMILY/BOARD/BRANCH/RELEASE, or per-host when NETBOOT_HOSTNAME
is set), normalize NETBOOT_CLIENT_MAC to PXELINUX 01-<mac> form,
fail fast on bad ROOTFS_COMPRESSION/ROOTFS_EXPORT_DIR combinations.
- custom_kernel_config: enable ROOT_NFS, NFS_FS, NFS_V3, IP_PNP,
IP_PNP_DHCP so root=/dev/nfs ip=dhcp works without an initrd.
- post_customize_image: drop armbian-resize-filesystem.service
(meaningless on NFS root) and /root/.not_logged_in_yet (the
armbian-firstlogin interactive wizard blocks bring-up when there is
no interactive console). armbian-firstrun.service stays — it only
regenerates SSH host keys.
- host_pre_docker_launch: append a bind-mount for ROOTFS_EXPORT_DIR to
DOCKER_EXTRA_ARGS when the directory lives outside ${SRC}, using the
hook's documented mechanism.
- pre_umount_final_image: assemble the TFTP tree (Image/zImage, dtb/,
uInitrd), write pxelinux.cfg/{default.example | 01-<mac>} with the
right FDT/FDTDIR line and explicit INITRD directive when uInitrd is
present, expose a netboot_artifacts_ready hook for userpatches.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New rootfs type for full network boot: the only thing on the device's local storage is U-Boot itself. Kernel, DTB, optional uInitrd and PXE config come from TFTP; rootfs is mounted over NFS. A new case branch in do_main_configuration auto-enables the netboot extension, symmetric with existing fs-f2fs-support / fs-btrfs wiring. The legacy ROOTFS_TYPE=nfs (hybrid: kernel on local storage, only / over NFS) is untouched — both paths coexist. - nfs-root case branch in ROOTFS_TYPE dispatch calls enable_extension "netboot" - prepare_partitions skips root partition creation and SD-size sanity check - check_filesystem_compatibility_on_host skipped for nfs-root Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the netboot extension: artifact server setup (tftpd-hpa + nfs-kernel-server), TFTP tree layout, DHCP options 66/67 on the network DHCP server, userpatches.conf knobs, the netboot_artifacts_ready hook, a full end-to-end helios64 walkthrough, and a troubleshooting section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
a10feb4 to
4ff6016
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
Adds an opt-in
netbootextension that produces a full network-boot payload — kernel, DTB, optionaluInitrd,pxelinux.cfgand an NFS-exportable rootfs — from a regular Armbian build. For netboot the only thing that has to live on the device's local storage is U-Boot itself: the kernel and DTB come from TFTP,/is mounted over NFS, nothing else on mmc/eMMC/USB is touched during early boot.Today
ROOTFS_TYPE=nfsalone only gives a hybrid image (kernel+DTB still on SD/eMMC, only/over NFS). This PR keeps that path working and layers a real netboot flow on top via a newROOTFS_TYPE=nfs-root.What changes
lib/functions/configuration/main-config.sh— newROOTFS_TYPE=nfs-rootcase branch, symmetric with the existingfs-f2fs-support/fs-btrfswiring: the branch auto-enables thenetbootextension soROOTFS_TYPE=nfs-rootis the single switch that selects the full netboot flow.check_filesystem_compatibility_on_hostis skipped for bothnfsandnfs-root— the host-side check is a sanity net for block-device targets and falsely rejects valid NFS configurations.extensions/netboot/netboot.sh— new, directory-based extension. Directory layout (rather than a singleextensions/netboot.shfile) exists so the long-form usage guide can live next to the code asREADME.md; inlining documentation of that size into the script body would be unwieldy. Hooks:extension_prepare_config— validate variables, compute defaults forNETBOOT_TFTP_PREFIX/NETBOOT_NFS_PATH(shared byLINUXFAMILY/BOARD/BRANCH/RELEASE, or per-host whenNETBOOT_HOSTNAMEis set), normalizeNETBOOT_CLIENT_MACto the01-<mac>PXELINUX form, fail fast on badROOTFS_COMPRESSION/ROOTFS_EXPORT_DIRcombinations before debootstrap.custom_kernel_config— enableROOT_NFS,NFS_FS,NFS_V3,IP_PNP,IP_PNP_DHCPsoroot=/dev/nfs ip=dhcpworks without an initrd.post_customize_image— droparmbian-resize-filesystem.service(meaningless on NFS root) and/root/.not_logged_in_yet(thearmbian-firstlogininteractive wizard blocks bring-up when there is no interactive console).armbian-firstrun.servicestays — it only regenerates SSH host keys.host_pre_docker_launch— bind-mountROOTFS_EXPORT_DIRinto the build container when it lives outside${SRC}, so the single-step builder-as-NFS-server workflow works the same inside and outside Docker.pre_umount_final_image— assemble the TFTP tree (Image/zImage,dtb/,uInitrd), writepxelinux.cfg/{default.example | 01-<mac>}with the rightFDT/FDTDIRline and an explicitINITRDdirective whenuInitrdis present (U-Boot's PXE parser only loads an initramfs when the stanza names it), expose anetboot_artifacts_readyhook for userpatches that deploy to a real server.lib/functions/image/rootfs-to-image.sh— two new controls for NFS-rootfs builds (bothROOTFS_TYPE=nfsandnfs-root):ROOTFS_COMPRESSION=gzip|zstd|none(defaultgzip).gzip→.tar.gz,zstd→.tar.zst,none→ no archive at all. Rejected early ifnoneis set withoutROOTFS_EXPORT_DIR. Thetar | pv | compressorpipeline now runs in aset -o pipefailsubshell so a broken archive step actually fails the build instead of producing a silently truncated file.ROOTFS_EXPORT_DIR=/abs/path— also (or only) rsync the rootfs tree into this directory. Can point anywhere on the build host's filesystem — inside or outside the Armbian tree (the extension bind-mounts external paths into the Docker container automatically). If the path is an NFS mount or any other network filesystem, the build writes directly to the remote server with no intermediate archive. The rsync uses--deleteso a re-used export dir stays in sync with the new build instead of accumulating stale files from previous runs. Combined withROOTFS_COMPRESSION=nonethis turns deploy-to-NFS-server into a single build step.ROOTFS_ARCHIVE_PATHis exported so downstream hooks (the newnetboot_artifacts_ready) can reference the produced archive.lib/functions/image/partitioning.sh—prepare_partitionsskips the root partition entry fornfs-rootthe same way it does fornfs, and the SD-size sanity check no longer applies to either (there is no image file to size).config/templates/nfs-boot.cmd.template— previously sunxi/arm32-only (zImage+bootz). Made arch-agnostic so the hybridROOTFS_TYPE=nfspath builds for arm64 boards too.extensions/netboot/README.md— long-form guide: variable reference, server setup (tftpd-hpa+nfs-kernel-server), DHCP 66/67 config (OpenWRT + others), per-host / per-MAC deployments, firstrun vs firstlogin, end-to-end helios64 example, troubleshooting.Variables (quick reference)
All optional.
ROOTFS_TYPE=nfs-rootalone gives you a shared-rootfs build with apxelinux.cfg/default.examplefile.NETBOOT_SERVERnfsroot=keeps${serverip}literal for U-Boot to fill from DHCP siaddr.NETBOOT_TFTP_PREFIXarmbian/${LINUXFAMILY}/${BOARD}/${BRANCH}-${RELEASE}NETBOOT_NFS_PATH/srv/netboot/rootfs/shared/...or/srv/netboot/rootfs/hosts/<hostname>nfsroot=.NETBOOT_HOSTNAMEhosts/<hostname>/— each machine owns its own writable copy.NETBOOT_CLIENT_MAC01-<mac>(per-MAC PXELINUX override). Accepts:or-, normalized to lowercase.ROOTFS_COMPRESSIONgzipgzip/zstd/none.ROOTFS_EXPORT_DIRTest plan
Validated end-to-end on helios64 (
rockchip64,edge/resolute,ttyS2 @ 1500000), Armbian 26.05:ROOTFS_TYPE=nfs-root NETBOOT_SERVER=192.168.1.125, shared NFS path, FDTDIR fallback for boards withoutBOOT_FDT_FILE, no hardcodedconsole=). 6:57 min, all artifacts correct.NETBOOT_HOSTNAME=helios64-a NETBOOT_CLIENT_MAC=aa:bb:cc:dd:ee:ff).pxelinux.cfg/01-aa-bb-cc-dd-ee-ffcontainsnfsroot=.../hosts/helios64-a. 7:44 min.ROOTFS_COMPRESSION=zstd— produces.tar.zst,ROOTFS_ARCHIVE_PATHset correctly. 460 MB vs 503 MB gzip.ROOTFS_COMPRESSION=none ROOTFS_EXPORT_DIR=...— single-step tree workflow, 1.5 GB tree in export dir, no archive produced. 5:17 min.ROOTFS_COMPRESSION=nonewithoutROOTFS_EXPORT_DIR— fails fast inextension_prepare_configbefore debootstrap, not hours later.tftpd-hpa+ NFS rootfs via rsync tonfs-kernel-server. DHCP options 66/67 on OpenWRT. Helios64 cold boot → U-Bootbootflow scan -lb→ TFTP →booti→ kernel →ip_auto_config→ NFS mount → systemdgraphical.target→ login prompt onttyS2@1500000. Noarmbian-firstloginwizard, noresize2fserrors,armbian-firstrun.serviceregenerates SSH host keys normally. Verified twice: once with archive workflow + manual unpack, once with tree workflow + rsync deploy.mvebu,armhf, U-Boot v2025.10) — second SoC family. PXE netboot: DHCP → TFTP (zImage + DTB) →bootz→ kernel → NFS root → systemd → login prompt. No initrd needed (see notes below).BOOT_FDT_FILEset (the FDTDIR vs FDT code path is exercised by helios64's missingBOOT_FDT_FILE, but a board with it set should also work).Known issues found during helios4 testing (not blockers for this PR):
ramdisk_addr_r(0x2880000) is only 8 MB abovekernel_addr_r(0x2080000), but zImage 6.12 is 8.1 MB → U-Boot refuses to boot with initrd. Workaround: omitINITRDfrom PXE config — kernel withCONFIG_ROOT_NFS=ymounts NFS root directly without initrd.ip=dhcpwrites DNS to/proc/net/pnpbutsystemd-resolveddoesn't read it → "No DNS servers known". Workaround:resolvectl dns <iface> <gateway>. Needs a networkd.networkfile or oneshot service inpost_customize_image(future improvement).Post-review update: architecture reworked after CodeRabbit #1 — the extension no longer forces
ROOTFS_TYPEfrom insideextension_prepare_config(too late in the config lifecycle); instead a newROOTFS_TYPE=nfs-rootcase branch inmain-config.shenables the extension, symmetric with existing filesystem-support extensions.pipefailon the archive pipeline (#4),rsync --deleteon the export dir (#5), and an explicitINITRDdirective whenuInitrdis present (#2) are all in.Related work
A companion PR to armbian/documentation will add
Developer-Guide_Netboot.mdwith the short overview + variable reference; this extension'sREADME.mdis the long-form guide and is linked from that page.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Documentation