#!/bin/bash # Setup script for iPXE boot server # Creates directory structure and deploys Quadlet set -euo pipefail # Colors RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[0;33m' NC='\033[0m' log_info() { printf "${BLUE}[INFO]${NC} %s\n" "$1"; } log_ok() { printf "${GREEN}[OK]${NC} %s\n" "$1"; } log_warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$1"; } log_err() { printf "${RED}[ERROR]${NC} %s\n" "$1"; } # Configuration IPXE_BASE="${IPXE_BASE:-$HOME/ipxe}" QUADLET_DIR="${QUADLET_DIR:-$HOME/.config/containers/systemd}" CADDY_SNIPPETS="${CADDY_SNIPPETS:-$HOME/Caddy/snippets}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" usage() { cat << EOF iPXE Boot Server Setup Usage: $0 [command] Commands: install Install Quadlet and create directories build Build the container image start Start the iPXE server stop Stop the iPXE server status Show server status logs Show server logs uninstall Remove Quadlet unit Options: --rootless Install for rootless Podman (default) --root Install system-wide (requires sudo) EOF } create_directories() { log_info "Creating directory structure..." mkdir -p "$IPXE_BASE"/{boot,menus,images,config} mkdir -p "$QUADLET_DIR" mkdir -p "$CADDY_SNIPPETS" log_ok "Directories created at $IPXE_BASE" } install_quadlet() { log_info "Installing Quadlet unit..." # Copy Quadlet file cp "$SCRIPT_DIR/ipxe-server.container" "$QUADLET_DIR/" # Update paths in Quadlet to use actual home directory sed -i "s|%h/ipxe|$IPXE_BASE|g" "$QUADLET_DIR/ipxe-server.container" log_ok "Quadlet installed to $QUADLET_DIR/ipxe-server.container" } install_caddy_snippet() { log_info "Installing Caddy snippet..." cp "$SCRIPT_DIR/snippets/ipxe" "$CADDY_SNIPPETS/" log_ok "Caddy snippet installed to $CADDY_SNIPPETS/ipxe" log_warn "Don't forget to:" log_warn " 1. Add 'import snippets/ipxe' to your Caddyfile" log_warn " 2. Add the ipxe.nuc.lan site block from caddy/ipxe.caddyfile" log_warn " 3. Reload Caddy: podman exec caddy caddy reload --config /etc/caddy/Caddyfile" } copy_default_menu() { log_info "Copying default boot menu..." if [[ ! -f "$IPXE_BASE/menus/boot.ipxe" ]]; then # Extract default menu from Containerfile or use embedded one cat > "$IPXE_BASE/menus/boot.ipxe" << 'EOF' #!ipxe # iPXE Boot Menu - NUC Home Server set menu-timeout 30000 set menu-default local :start menu iPXE Boot Menu - nuc.lan item --gap -- -------- Boot Options -------- item local Boot from local disk item --gap -- -------- Network Boot -------- item fedora-live Fedora 42 Live (Minimal) item fedora-kiosk Fedora 42 Kiosk/PoS item --gap -- -------- Utilities -------- item shell iPXE Shell item reboot Reboot choose --timeout ${menu-timeout} --default ${menu-default} selected || goto cancel goto ${selected} :cancel echo Boot cancelled goto start :local sanboot --no-describe --drive 0x80 || goto start :fedora-live echo Booting Fedora 42 Live... set base-url http://ipxe.nuc.lan/images/fedora-42 kernel ${base-url}/vmlinuz initrd=initrd.img root=live:${base-url}/squashfs.img rd.live.image quiet initrd ${base-url}/initrd.img boot || goto start :fedora-kiosk echo Booting Fedora 42 Kiosk... set base-url http://ipxe.nuc.lan/images/fedora-42-kiosk kernel ${base-url}/vmlinuz initrd=initrd.img root=live:${base-url}/squashfs.img rd.live.image quiet initrd ${base-url}/initrd.img boot || goto start :shell shell :reboot reboot EOF log_ok "Default boot menu created" else log_warn "Boot menu already exists, skipping" fi } build_image() { log_info "Building iPXE server container image..." podman build -t localhost/ipxe-server:latest -f "$SCRIPT_DIR/Containerfile" "$SCRIPT_DIR" log_ok "Image built: localhost/ipxe-server:latest" } reload_systemd() { log_info "Reloading systemd user units..." systemctl --user daemon-reload log_ok "Systemd reloaded" } start_server() { log_info "Starting iPXE server..." systemctl --user start ipxe-server log_ok "iPXE server started" } stop_server() { log_info "Stopping iPXE server..." systemctl --user stop ipxe-server log_ok "iPXE server stopped" } show_status() { systemctl --user status ipxe-server } show_logs() { journalctl --user -u ipxe-server -f } uninstall() { log_info "Uninstalling iPXE server..." systemctl --user stop ipxe-server 2>/dev/null || true rm -f "$QUADLET_DIR/ipxe-server.container" systemctl --user daemon-reload log_ok "Quadlet removed" log_warn "Data in $IPXE_BASE preserved. Remove manually if needed." } do_install() { create_directories install_quadlet install_caddy_snippet copy_default_menu build_image reload_systemd echo "" log_ok "Installation complete!" echo "" echo "Next steps:" echo " 1. Add boot files to: $IPXE_BASE/boot/" echo " 2. Add images to: $IPXE_BASE/images/" echo " 3. Edit boot menu: $IPXE_BASE/menus/boot.ipxe" echo " 4. Update Caddy config (see caddy/ipxe.caddyfile)" echo " 5. Start server: $0 start" echo "" } # Main case "${1:-}" in install) do_install ;; build) build_image ;; start) start_server ;; stop) stop_server ;; status) show_status ;; logs) show_logs ;; uninstall) uninstall ;; -h|--help|help|"") usage ;; *) log_err "Unknown command: $1" usage exit 1 ;; esac