feat(pxe-boot): add Debian bookworm minimal netboot menu entry

Adds a `fetch-debian-netboot.service` oneshot that downloads the Debian
bookworm netboot kernel and initrd from deb.debian.org on first boot,
stages them under /srv/pxe/http/debian/, and serves them via a generated
debian.ipxe chain script. The service is idempotent — it skips the
download if both files are already present.

Also merges the previously split systemd.tmpfiles.rules and
systemd.services blocks into a single systemd = { ... } attrset to
satisfy statix's repeated-keys lint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 03:23:14 +10:00
co-authored by Claude Sonnet 4.6
parent bf88a6ebb0
commit f7c32aff12
2 changed files with 115 additions and 27 deletions
+29
View File
@@ -15,6 +15,7 @@ rescue/inspection use.
- TFTP root for first-stage bootloaders: `/srv/pxe/tftp` - TFTP root for first-stage bootloaders: `/srv/pxe/tftp`
- iPXE entry script: `/srv/pxe/http/boot.ipxe` - iPXE entry script: `/srv/pxe/http/boot.ipxe`
- Generated iPXE menu: `/srv/pxe/http/menu.ipxe` - Generated iPXE menu: `/srv/pxe/http/menu.ipxe`
- Debian Minimal iPXE script: `/srv/pxe/http/debian.ipxe`
- SystemRescue iPXE script: `/srv/pxe/http/systemrescue.ipxe` - SystemRescue iPXE script: `/srv/pxe/http/systemrescue.ipxe`
- TFTP fallback script: `/srv/pxe/tftp/autoexec.ipxe` - TFTP fallback script: `/srv/pxe/tftp/autoexec.ipxe`
- Boot binaries copied from the Nix `ipxe` package: - Boot binaries copied from the Nix `ipxe` package:
@@ -31,6 +32,7 @@ The host creates these directories with systemd tmpfiles:
/srv/pxe/http/images /srv/pxe/http/images
/srv/pxe/http/auto-installer /srv/pxe/http/auto-installer
/srv/pxe/http/nixos-minimal /srv/pxe/http/nixos-minimal
/srv/pxe/http/debian
/srv/pxe/http/systemrescue /srv/pxe/http/systemrescue
/srv/pxe/http/ubuntu /srv/pxe/http/ubuntu
/srv/pxe/http/rescue /srv/pxe/http/rescue
@@ -54,6 +56,7 @@ The generated menu currently exposes entries for:
- NixOS Auto-Installer - NixOS Auto-Installer
- NixOS Minimal - NixOS Minimal
- Debian Minimal
- SystemRescue environment - SystemRescue environment
- iPXE shell - iPXE shell
- Reboot - Reboot
@@ -84,6 +87,29 @@ directory name (`auto-installer` / `nixos-minimal`), so each one's
generated system name (`nixos-system-<name>-*`) is self-describing rather generated system name (`nixos-system-<name>-*`) is self-describing rather
than the nixpkgs default of `nixos-system-nixos-*` for both. than the nixpkgs default of `nixos-system-nixos-*` for both.
The Debian Minimal entry chains `http://<pxeServerIp>/debian.ipxe`, which loads
the Debian bookworm netboot kernel and initrd from `/srv/pxe/http/debian/`. The
`fetch-debian-netboot.service` oneshot downloads these files from
`deb.debian.org` on first boot (idempotent — skips if files are already
present):
```text
/srv/pxe/http/debian/linux (Debian bookworm netboot kernel)
/srv/pxe/http/debian/initrd.gz (Debian bookworm netboot initrd)
```
The service requires outbound internet access on the pxe-boot host. To
re-download (e.g. after a Debian point release), delete the files and restart
the service:
```bash
rm /srv/pxe/http/debian/linux /srv/pxe/http/debian/initrd.gz
systemctl restart fetch-debian-netboot.service
```
To update to a different Debian release, change `debianRelease` in
`modules/build-types/pxe-boot.nix` and redeploy.
The SystemRescue entry expects the source ISO at: The SystemRescue entry expects the source ISO at:
```text ```text
@@ -113,6 +139,9 @@ After deployment by an operator, basic service checks are:
```bash ```bash
curl http://pxe-boot/boot.ipxe curl http://pxe-boot/boot.ipxe
curl http://pxe-boot/menu.ipxe curl http://pxe-boot/menu.ipxe
curl http://pxe-boot/debian.ipxe
curl -I http://pxe-boot/debian/linux
curl -I http://pxe-boot/debian/initrd.gz
curl http://pxe-boot/systemrescue.ipxe curl http://pxe-boot/systemrescue.ipxe
curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/vmlinuz curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/vmlinuz
curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/sysresccd.img curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/sysresccd.img
+86 -27
View File
@@ -21,6 +21,39 @@ let
chain ${pxeBaseUrl}/boot.ipxe chain ${pxeBaseUrl}/boot.ipxe
''; '';
debianRelease = "bookworm";
debianMirror = "https://deb.debian.org/debian";
debianNetbootBase = "${debianMirror}/dists/${debianRelease}/main/installer-amd64/current/images/netboot/debian-installer/amd64";
debianIpxe = pkgs.writeText "debian.ipxe" ''
#!ipxe
set base ${pxeBaseUrl}
kernel ''${base}/debian/linux
initrd ''${base}/debian/initrd.gz
boot
'';
fetchDebianNetboot = pkgs.writeShellScript "fetch-debian-netboot" ''
set -eu
dir="${httpRoot}/debian"
mirror="${debianNetbootBase}"
if [ -f "$dir/linux" ] && [ -f "$dir/initrd.gz" ]; then
echo "Debian ${debianRelease} netboot files already present; skipping download."
exit 0
fi
echo "Downloading Debian ${debianRelease} netboot kernel and initrd from $mirror ..."
${pkgs.curl}/bin/curl -fsSL -o "$dir/linux.tmp" "$mirror/linux"
${pkgs.curl}/bin/curl -fsSL -o "$dir/initrd.gz.tmp" "$mirror/initrd.gz"
mv "$dir/linux.tmp" "$dir/linux"
mv "$dir/initrd.gz.tmp" "$dir/initrd.gz"
echo "Debian ${debianRelease} netboot files staged."
'';
systemRescueIpxe = pkgs.writeText "systemrescue.ipxe" '' systemRescueIpxe = pkgs.writeText "systemrescue.ipxe" ''
#!ipxe #!ipxe
@@ -70,6 +103,7 @@ let
menu PXE Boot Menu menu PXE Boot Menu
item auto-installer NixOS Auto-Installer item auto-installer NixOS Auto-Installer
item nixos-minimal NixOS Minimal item nixos-minimal NixOS Minimal
item debian Debian Minimal
item rescue Rescue Environment item rescue Rescue Environment
item shell iPXE Shell item shell iPXE Shell
item reboot Reboot item reboot Reboot
@@ -82,6 +116,9 @@ let
:nixos-minimal :nixos-minimal
chain ''${base}/nixos-minimal/netboot.ipxe chain ''${base}/nixos-minimal/netboot.ipxe
:debian
chain ''${base}/debian.ipxe
:rescue :rescue
chain ''${base}/systemrescue.ipxe chain ''${base}/systemrescue.ipxe
@@ -129,34 +166,56 @@ in
openssh.settings.PermitRootLogin = "yes"; openssh.settings.PermitRootLogin = "yes";
}; };
systemd.tmpfiles.rules = [ systemd = {
"d ${pxeRoot} 0755 root root -" tmpfiles.rules = [
"d ${httpRoot} 0755 root root -" "d ${pxeRoot} 0755 root root -"
"d ${httpRoot}/images 0755 root root -" "d ${httpRoot} 0755 root root -"
"d ${httpRoot}/auto-installer 0755 root root -" "d ${httpRoot}/images 0755 root root -"
"d ${httpRoot}/nixos-minimal 0755 root root -" "d ${httpRoot}/auto-installer 0755 root root -"
"d ${httpRoot}/systemrescue 0755 root root -" "d ${httpRoot}/nixos-minimal 0755 root root -"
"d ${httpRoot}/ubuntu 0755 root root -" "d ${httpRoot}/systemrescue 0755 root root -"
"d ${httpRoot}/rescue 0755 root root -" "d ${httpRoot}/debian 0755 root root -"
"d ${tftpRoot} 0755 root root -" "d ${httpRoot}/ubuntu 0755 root root -"
"C+ ${httpRoot}/boot.ipxe 0644 root root - ${bootIpxe}" "d ${httpRoot}/rescue 0755 root root -"
"C+ ${httpRoot}/menu.ipxe 0644 root root - ${menuIpxe}" "d ${tftpRoot} 0755 root root -"
"C+ ${httpRoot}/systemrescue.ipxe 0644 root root - ${systemRescueIpxe}" "C+ ${httpRoot}/boot.ipxe 0644 root root - ${bootIpxe}"
"C+ ${tftpRoot}/autoexec.ipxe 0644 root root - ${autoexecIpxe}" "C+ ${httpRoot}/menu.ipxe 0644 root root - ${menuIpxe}"
"C+ ${tftpRoot}/ipxe.efi 0644 root root - ${pkgs.ipxe}/ipxe.efi" "C+ ${httpRoot}/debian.ipxe 0644 root root - ${debianIpxe}"
"C+ ${tftpRoot}/undionly.kpxe 0644 root root - ${pkgs.ipxe}/undionly.kpxe" "C+ ${httpRoot}/systemrescue.ipxe 0644 root root - ${systemRescueIpxe}"
]; "C+ ${tftpRoot}/autoexec.ipxe 0644 root root - ${autoexecIpxe}"
"C+ ${tftpRoot}/ipxe.efi 0644 root root - ${pkgs.ipxe}/ipxe.efi"
systemd.services.stage-systemrescue = { "C+ ${tftpRoot}/undionly.kpxe 0644 root root - ${pkgs.ipxe}/undionly.kpxe"
description = "Stage SystemRescue ISO contents for HTTP PXE boot";
after = [
"local-fs.target"
"systemd-tmpfiles-setup.service"
]; ];
wantedBy = [ "multi-user.target" ];
serviceConfig = { services = {
Type = "oneshot"; fetch-debian-netboot = {
ExecStart = stageSystemRescue; description = "Download Debian ${debianRelease} netboot kernel and initrd for HTTP PXE boot";
after = [
"local-fs.target"
"systemd-tmpfiles-setup.service"
"network-online.target"
];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = fetchDebianNetboot;
RemainAfterExit = true;
};
};
stage-systemrescue = {
description = "Stage SystemRescue ISO contents for HTTP PXE boot";
after = [
"local-fs.target"
"systemd-tmpfiles-setup.service"
];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = stageSystemRescue;
};
};
}; };
}; };