skills/ubuntu-secureboot-pxe-netboot/SKILL.md
Build a UEFI Secure Boot PXE netboot server for Ubuntu autoinstall. Use when: designing or implementing network boot infrastructure for automated Ubuntu provisioning with Secure Boot enabled. Covers the complete chain: signed shim+GRUB selection, TFTP layout, kernel parameters, autoinstall config requirements, and post-install bootstrapping scripts. Also applicable when debugging an existing PXE setup that uses the wrong GRUB binary or config paths.
npx skillsauth add aldengolab/lorist ubuntu-secureboot-pxe-netbootInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Building a PXE netboot server for Ubuntu with UEFI Secure Boot requires assembling components from multiple sources with specific, undocumented compatibility requirements. Getting any piece wrong produces silent failures or misleading errors.
UEFI Firmware (PXE client)
-> DHCP proxy (dnsmasq) provides boot filename + TFTP server
-> TFTP: BOOTx64.EFI (signed shim)
-> TFTP: grubx64.efi (signed NETBOOT GRUB -- not local-boot GRUB)
-> TFTP: grub/grub.cfg (GRUB menu)
-> HTTP: ubuntu-live-server.iso (full ISO for live filesystem)
-> HTTP: autoinstall/user-data (cloud-init autoinstall config)
The ISO contains everything needed, but you must select the right binaries:
| Asset | ISO Path | Purpose |
|-------|----------|---------|
| Signed shim | EFI/boot/bootx64.efi | First-stage bootloader, validates GRUB signature |
| Signed netboot GRUB | pool/main/g/grub2-signed/grub-efi-amd64-signed_*.deb -> grubnetx64.efi.signed | GRUB with tftp/http/efinet modules |
| Kernel | casper/vmlinuz | Linux kernel |
| Initrd | casper/initrd | Initial ramdisk |
| Live filesystem | The ISO itself | Contains filesystem.squashfs for the installer |
Critical: Do NOT use EFI/boot/grubx64.efi -- this is the local-boot GRUB
without network protocol modules. It will load via UEFI firmware's PXE stack but
cannot fetch files over TFTP/HTTP on its own.
Extract the netboot GRUB from the deb in the ISO's package pool:
GRUB_DEB=$(bsdtar -tf "$ISO_FILE" | grep 'pool/main/g/grub2-signed/grub-efi-amd64-signed_.*\.deb$')
bsdtar -xf "$ISO_FILE" -C /tmp "$GRUB_DEB"
dpkg-deb -x "/tmp/${GRUB_DEB}" /tmp/grub-signed
cp /tmp/grub-signed/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed "${DEST}/grubx64.efi"
The netboot GRUB binary uses prefix /grub (not /boot/grub):
/srv/tftp/
BOOTx64.EFI # Signed shim (renamed to uppercase)
grubx64.efi # Signed NETBOOT GRUB (grubnetx64.efi.signed)
grub/grub.cfg # GRUB config ($prefix/grub.cfg)
Verification: dnsmasq TFTP logs will show GRUB requesting
grub/x86_64-efi/command.lst -- the directory before the filename confirms
the prefix.
set timeout=5
set default=0
menuentry "Boot from local disk" {
exit
}
menuentry "Ubuntu Autoinstall" {
linux (http,SERVER_IP:PORT)/ubuntu/vmlinuz \
url=http://SERVER_IP:PORT/ubuntu/ubuntu-live-server.iso \
autoinstall ds=nocloud-net\;seedfrom=http://SERVER_IP:PORT/autoinstall/ \
ip=dhcp ---
initrd (http,SERVER_IP:PORT)/ubuntu/initrd
}
Key parameters:
url= -- points to the full ISO; the installer downloads and mounts it for
filesystem.squashfs (just vmlinuz+initrd is not enough)ip=dhcp -- ensures networking is up before the ISO downloadds=nocloud-net;seedfrom= -- tells cloud-init where to fetch autoinstall configRequired fields for fully unattended install:
autoinstall:
version: 1
interactive-sections: [] # REQUIRED -- suppresses OOBE wizard
locale: en_US.UTF-8
keyboard:
layout: us
storage:
layout:
name: direct
identity:
hostname: ...
username: ...
password: ... # SHA-512 hash ($6$...)
ssh:
install-server: true
authorized-keys:
- ...
network:
version: 2
ethernets:
all-en:
match:
name: en*
dhcp4: true
packages:
- jq # or any packages needed by late-commands
late-commands:
- ... # post-install bootstrapping
Without interactive-sections: [], Ubuntu 24.04 shows a first-boot setup
wizard even with a complete config, which interrupts late-commands execution.
curtin in-target -- runs commands inside a chroot of the installed system.
The chroot does NOT have:
/sys/firmware/efi/efivars -- efibootmgr fails silentlyRule of thumb:
curtin in-targetcurtin in-target)When writing scripts that use curl | sh install patterns (k3s, Tailscale, etc.),
export environment variables instead of inline assignment:
# UNRELIABLE -- vars may not pass through pipe
curl -sfL https://example.com/install.sh | MY_VAR="value" sh -
# RELIABLE -- export ensures vars reach the piped script
export MY_VAR="value"
curl -sfL https://example.com/install.sh | sh -
For k3s specifically, INSTALL_K3S_EXEC="agent" must be exported to avoid
installing as a server node.
port=0 # Disable DNS
dhcp-range=192.168.0.0,proxy # Proxy mode -- no IP allocation
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-match=set:efi-x86_64,option:client-arch,9
pxe-service=tag:efi-x86_64,x86-64_EFI,"PXE Boot",BOOTx64.EFI
dhcp-boot=tag:efi-x86_64,BOOTx64.EFI,,SERVER_IP
enable-tftp
tftp-root=/srv/tftp
sent grubx64.efi (not just BOOTx64.EFI)grub/grub.cfg (confirms correct prefix)When things go wrong, read references/troubleshooting.md for symptom-based diagnostics:
revocations.efi not found in TFTP logs is expected (shim SBAT check, non-fatal)grubnetx64.efi.signed is Canonical-signed -- shim trusts it with Secure Bootdevelopment
Design pattern for running a persistent PXE/TFTP server that safely coexists with already-installed nodes. Use when: building PXE infrastructure that should stay always-on, designing automated bare-metal provisioning in GitOps/Kubernetes environments, or any PXE setup where UEFI boot order has network boot first. Eliminates boot loops without requiring UEFI firmware changes.
development
This skill governs all prose output — Claude's own responses, documentation, PR descriptions, commit messages, README content, comments, and any text the user asks to draft or edit. It should also be used when the user asks to "review my writing", "edit this for clarity", "make this clearer", "simplify this text", "rewrite this", "check my prose", "tighten this up", or "make this more concise". Based on George Orwell's "Politics and the English Language" (1946).
development
Debug Kubernetes pods using hostNetwork: true that crash with "Address already in use" or "failed to create listening socket for port N". Use when: (1) a hostNetwork pod container is in CrashLoopBackOff and logs show a port bind failure, (2) the port works fine in non-hostNetwork pods but fails with hostNetwork, (3) you need to identify which host-level process holds a port from within Kubernetes (no SSH). Covers /proc/net/udp inspection and kubectl debug node with nsenter.
development
Adversarial 3-stage review of an implementation plan before execution — checks completeness, security/best-practices, and multi-agent implementability. Use this skill whenever the user wants to validate, review, stress-test, or check an existing implementation plan before building or executing it. Trigger on phrases like "review the plan", "stress-test the plan", "is this plan ready", "check the plan", "validate the plan", "go through the plan", "find what's wrong with the plan", "is this ready for agents", "can subagents execute this", or any request to find gaps, security issues, or underspecified tasks in a plan that has already been written. Also trigger when the user has just finished /plan-implementation and is about to run /autonomous-plan-execution — the plan should be reviewed first. This skill is specifically for reviewing EXISTING plans, not for writing new ones (use plan-implementation for that), not for code review or PR review (use code-review for that), and not for debugging or post-deployment issues. If the user mentions a plan file, says "before we build", or asks whether phases can be parallelized, this skill almost certainly applies.