Setting Up Network Booting of Stateless Clients

Hello everyone,

I’ve been working on a network-booted, diskless client environment, drawing inspiration from the excellent work over at Level1Techs. I wanted to share some insights into the setup I’m using and a bit about the script that’s helping bring it all together. My setup is geared for high scalability and customization, and I’m hoping this can be useful to others or spark some feedback on ways to make it even better.

Project Overview

The environment I’ve set up is a stateless network-booted system where each client device boots a live Linux environment directly over the network. Unlike the Raspberry Pi-based setups some may have seen, this configuration uses more powerful clients with custom root filesystems. Here’s a high-level view of how it all works:

1.	Master Server: Instead of pfSense or TrueNAS, a raw Linux machine acts as the master server, managing network configuration, DHCP, DNS, and NFS.
2.	Custom Root Filesystems: Each client has a unique root filesystem mapped on the NAS. This allows clients to have isolated environments while still being centrally managed. The filesystem paths are set up as /mnt/raid5/netos/disk/<mac-address>.img, which provides a custom environment per device.
3.	Network-Booted Live System: The system boots clients via PXE, with custom-built initrd and root filesystem images. The entire OS runs as a read-only live system, which helps maintain stability and security across all devices.
4.	DNS and NFS Optimization: DNS leases are managed through a private domain, which simplifies hostname resolution across clients. Since the root filesystem is mounted over NFS, I’ve worked around common stability issues by configuring a post-switch_root script that refreshes DNS settings, ensuring smooth connectivity.

Script Highlights

The script I’ve created is designed to automate much of this setup. Here are some of the main functions it handles:

•	Network Configuration: Sets up the initial environment with DHCP and DNS for each diskless client, allowing them to connect seamlessly.
•	Root Filesystem Allocation: The script links each client’s MAC address to a unique root filesystem path, automating the deployment of isolated environments.
•	NFS and DNS Handling: After booting, the script ensures the client’s NFS root filesystem remains stable, handling any reconnections and DNS updates needed post-switch_root.

Goals and Future Plans

While the setup is stable and has been working well, I’m exploring additional ways to optimize NFS performance and enhance scalability. The goal is to make this environment flexible enough for various use cases, from lab testing setups to potentially scaled enterprise configurations.

If anyone has suggestions on optimizing networked root filesystems, or ideas on handling larger, more dynamic client environments, I’d love to hear them. Also, feel free to ask if you’re curious about any specific aspect of the setup or the script!

Thanks for reading, and looking forward to any thoughts!

Best,
Max

Here is the bash script i made that setups the “netos” environment:
#!/bin/bash
rm -rf build.log # remove any old build logs
set -e
set -o pipefail
{ cleanup() {
set +e # disable ‘set -e’ during cleanup to avoid failures in cleanup
if read -t 3 -p “Press any key in the next 3 seconds to cancel cleanup …” -r _; then
echo “Cleanup canceled by user”
return; fi
echo; echo “No input detected. Proceeding with cleanup…”
if [[ “$section” == “0” ]]; then
echo “Nothing to clean”
return; fi
echo “Cleaning packages”
while true; do
read -p “Do you want to remove installed packages? (y/n) [N]: " -r remove_packages
remove_packages=${remove_packages:-n}
if [ “$remove_packages” == “y” ]; then
for pkg in “${packages[@]}”; do
if dpkg-query -W -f=‘${Status}’ “$pkg” 2>/dev/null | grep -q “install ok installed”; then
echo “$pkg is installed”
apt-get -y purge “$pkg”
else
echo “$pkg is not installed”; fi; done
apt-get -y autoremove
apt-get clean
elif [ “$remove_packages” == “n” ]; then
break
else
echo “Invalid option. Please type ‘y’ or ‘n’.”
continue; fi
break; done
echo “Packages cleaned”
if [[ “$section” == “1” ]]; then
echo “Cleanup complete”
return; fi
echo “Cleaning $netos_path”
rm -rf “$netos_path”
echo “$netos_path cleaned”
if [[ “$section” == “2” ]]; then
echo “Cleanup complete”
return; fi
if [ “$remove_packages” == “y” ]; then
echo “Cleanup complete”
return; fi
echo “Cleaning packages configs”
rm -f /etc/nginx/sites-enabled/netos
systemctl reload nginx
rm -f “$dnsmasq_conf”
systemctl stop dnsmasq
if [[ -f “$tftp_conf.bak” ]]; then
mv “$tftp_conf.bak” “$tftp_conf”
systemctl restart tftpd-hpa; fi
sed -i “|$netos_path|d” “/etc/exports”
exportfs -a
systemctl restart nfs-kernel-server
echo “Package configs cleaned”
if [[ “$section” == “3” ]]; then
echo “Cleanup complete”
return; fi; }
while getopts “r” option; do
case $option in
r) read -e -p “Where is netos? " -r file_path
file_path=”${file_path%%/}”
if [[ “$file_path” == */netos ]]; then
netos_path=“$file_path”
else
netos_path=“$file_path/netos”; fi
[ -z “$file_path” ] && netos_path=“/netos”
if [ ! -d “$netos_path” ]; then
echo “Directory ‘$netos_path’ does not exists.”
else
section=“3”
exports_file=“/etc/exports”
tftp_conf=“/etc/default/tftpd-hpa”
dnsmasq_conf=“/etc/dnsmasq.d/pxe.conf”
cleanup; fi
exit 1;;
*) echo “Invalid option: -$OPTARG”
exit 1;; esac; done
section=“0”
trap cleanup EXIT
echo “Starting netos build”
if [ “$EUID” -ne 0 ]; then
echo “This script must be run as root or with sudo”
exit 1; fi
if [ ! -f /etc/debian_version ]; then
echo “Unsupported operating system. This script only supports Debian-based systems”
exit 1; fi
echo “Detected Debian-based system”
section=“1”
packages=(“nfs-kernel-server” “nfs-common” “tftpd-hpa” “tftp” “nginx” “live-build” “syslinux” “pxelinux” “dnsmasq”) # packages we need
apt_updated=false
for pkg in “${packages[@]}”; do
if dpkg-query -W -f=‘${Status}’ “$pkg” 2>/dev/null | grep -q “install ok installed”; then
echo “$pkg is installed”
else
if [ “$apt_updated” = false ]; then
echo “Packages need to be installed, running apt update…”
apt-get update
apt_updated=true; fi
echo “$pkg is NOT installed, installing it”
apt-get install -y “$pkg” >> build.log
echo “$pkg installation complete”; fi; done
apt-get clean
echo “All dependices are installed, setting up enviorment”
check_nfs_kernel_support() {
if modprobe -n nfsd >/dev/null 2>&1; then
echo “NFS kernel support is available.”
return 0
else
echo “NFS kernel support is NOT available.”
return 1; fi; }
if check_nfs_kernel_support; then
echo “This system is capable of running NFS server functions.”
else
echo “This system is NOT capable of running NFS server functions.”
echo “NFS kernel functions are required to run an NFS server. You may be in an environment (e.g., a container) where this is restricted.”
exit 1; fi
read -e -p “Enter location for netos: " -r file_path
file_path=”${file_path%%/}"
if [[ “$file_path” == /netos ]]; then
netos_path=“$file_path”
else
netos_path=“$file_path/netos”; fi
[ -z “$file_path” ] && netos_path=“/netos”
if [ -d “$netos_path” ]; then
echo “Directory ‘$netos_path’ exists.”
else
echo “Directory ‘$netos_path’ does not exist, creating it now…”
if mkdir -p “$netos_path”; then
echo “Created successfully.”
else
echo “Failed to create directory ‘$netos_path’. Exiting.”
exit 1; fi; fi
section=“2”
mkdir -p “$netos_path”/{build,disks,http,rootfs,tftp}
ldlinux_path=$(find /usr/lib/ -name ldlinux.c32 2>/dev/null | head -n 1)
if [ -z “$ldlinux_path” ]; then
echo “ldlinux.c32 not found!”
else
echo “Copying $ldlinux_path to $netos_path/tftp”
cp “$ldlinux_path” “$netos_path/tftp”; fi
lpxelinux_path=$(find /usr/lib -name lpxelinux.0 2>/dev/null | head -n 1)
if [ -z “$lpxelinux_path” ]; then
echo “lpxelinux.0 not found!”
else
echo “Copying $lpxelinux_path to $netos_path/tftp”
cp “$lpxelinux_path” “$netos_path/tftp”; fi
mkdir -p “$netos_path/tftp/pxelinux.cfg”
ip=$(hostname -I | awk ‘{print $1}’)
while true; do
read -p “Do you want to use the IP address of this machine? (y/n) [Y]: " -r use_machine_ip
use_machine_ip=${use_machine_ip:-y}
if [ “$use_machine_ip” == “y” ]; then
ip_address=$ip
echo “Detected IP address: $ip_address”
elif [ “$use_machine_ip” == “n” ]; then
read -p “Please enter the IP address or DNS name you want to use: " -r ip_address
else
echo “Invalid option. Please type ‘y’ or ‘n’.”
continue; fi
break; done
echo “Using IP address: $ip_address”
read -p “Please enter a username for clients: " -r username
config_dir=”$netos_path/configs”
mkdir -p “$config_dir”
cat > “$config_dir/default” << EOF
DEFAULT linux
LABEL linux
KERNEL http://$ip_address:7777/vmlinuz
INITRD http://$ip_address:7777/initrd.img
APPEND root=/dev/nfs nfsroot=$ip:$netos_path/rootfs ip=dhcp ro boot=live username=$username components splash
EOF
echo “Made $config_dir/default”
echo “Edit this file for advanced configurations”
mkdir -p “$netos_path/build/auto”
cat > “$config_dir/lb-config” << ‘EOF’
#!/bin/bash
lb config noauto
–mirror-bootstrap “Index of /ubuntu
–distribution “jammy”
–mirror-binary “Index of /ubuntu
–bootloader “pxelinux”
–backports true
–archive-areas “universe main”
–mode “ubuntu”
–initsystem “systemd”
–system “live”
–net-tarball false
–zsync false
–memtest “none”
–initramfs “live-boot”
–binary-image “none”
–chroot-filesystem “squashfs”
“${@}”
EOF
chmod +x “$config_dir/lb-config”
echo “Made $config_dir/lb-config”
echo “This file configures general options for lb build”
authorized_keys_path=”$config_dir/authorized_keys"
if [ -f “$HOME/.ssh/id_rsa.pub” ]; then
cat “$HOME/.ssh/id_rsa.pub” >> “$authorized_keys_path”
echo “Added this machine’s SSH key to authorized_keys.”
else
echo “No SSH key found for this machine. Generating a new one…”
ssh-keygen -t rsa -b 4096 -f “$HOME/.ssh/id_rsa” -N “”
cat “$HOME/.ssh/id_rsa.pub” >> “$authorized_keys_path”
echo “Generated and added this machine’s SSH key to authorized_keys.”; fi
while true; do
read -p "Do you want to import your own SSH keys? (y/n) [N]: " -r choice
choice=${choice:-n}
if [ “$choice” == “y” ]; then
while true; do
echo “Please paste your SSH key:”
read -r user_ssh_key
echo “$user_ssh_key” >> “$authorized_keys_path”
echo “Added your provided SSH key to authorized_keys.”
read -p "Do you want to add another SSH key? (y/n) [N]: " -r add_another
add_another=${add_another:-n}
if [ “$add_another” != “y” ]; then
break; fi; done
elif [ “$choice” == “n” ]; then
break
else
echo “Invalid option. Please type ‘y’ or ‘n’.”
continue; fi; done
echo “Made $config_dir/authorized_keys”
echo “Edit this to add or remove ssh keys for all clients”
while true; do
read -p “Do you register hostnames in your main DNS server? (y/n) [N]: " -r register_dns
register_dns=${register_dns:-n}
if [ “$register_dns” == “y” ]; then
read -p “Enter the domain for the network (e.g., example.com): " -r network_domain
elif [ “$register_dns” == “n” ]; then
break
else
echo “Invalid option. Please type ‘y’ or ‘n’.”
continue; fi
break; done
if [[ -z “$network_domain” ]]; then
echo “No domain provided. Skipping.”
network_domain=“example.com”; fi
port=836
cat > “$config_dir/livemanage” << EOF
#!/bin/bash
IMAGE_DIR=”$netos_path/disks/”
REBOOT_PORT=$port
REBOOT_DELAY=5
declare -a hosts
echo “Welcome to the live management script!”
echo “Generating a list of reachable hosts…”
index=1
for img_file in ${IMAGE_DIR}
.img; do
hostname=“$(basename “$img_file” .img).${network_domain}”
if ping -c 1 -W 1 $hostname > /dev/null; then
hosts[index]=${hostname}
echo “Found host: ${hostname}”
((index++))
else
echo “Host $hostname is unreachable. Deleting associated image file.”
rm -f “$img_file”
echo “Deleted $img_file”; fi; done
hosts[${#hosts[@]} + 1]=“Cancel”
reboot_host() {
echo “Sending REBOOT signal to $1…”
echo “REBOOT” | nc “$1” “$REBOOT_PORT” -q 0
if timeout 2s nc -l -p 836; then
echo “Reboot signal sent to $1.”
else
echo “Failed to send reboot signal to $1.”; fi; }
host_menu() {
echo
if [[ $1 -eq 1 ]]; then
echo “SSH Menu:”; fi
if [[ $1 -eq 2 ]]; then
echo “Reboot Menu:”; fi
for index in “${!hosts[@]}”; do
echo “${index}) ${hosts[$index]}”; done
while true; do
read -p “#? " -r choice
selected_option=”${hosts[$choice]}"
if [ -z “$selected_option” ]; then
echo “Invalid option. Please choose a valid number.”
continue; fi
if [[ “$selected_option” == “Cancel” ]]; then
echo “Going back.”
return 0; fi
if [[ $1 -eq 1 ]]; then
ssh_address=“${username}@${selected_option}”
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=accept-new “${ssh_address}”
host_menu 1
break; fi
if [[ $1 -eq 2 ]]; then
reboot_host “$selected_option”
host_menu 2
break; fi; done; }
reboot_all_hosts () {
echo
for host in “${hosts[@]}”; do
if [ “$host” != “Cancel” ]; then
reboot_host “$host”
echo “Waiting ${REBOOT_DELAY} seconds before sending the next reboot signal…”
sleep “${REBOOT_DELAY}”; fi; done
echo “Reboot all hosts complete.”; }
while true; do
echo “”
echo “Main Menu:”
select choice in “SSH into a selected host” “Reboot a selected host” “Reboot all hosts” “Quit”; do
case $choice in
“SSH into a selected host”) host_menu 1; break;;
“Reboot a selected host”) host_menu 2; break;;
“Reboot all hosts”) reboot_all_hosts; break;;
“Quit”) echo “Exiting the script. Goodbye!”; exit 0;;
*) echo “Invalid option. Please choose a valid number.”;;
esac; done; done
EOF
chmod +x “$config_dir/livemanage”
echo “Made $config_dir/livemanage”
echo “This script allows you to easily perform some management on your all your clients”
cat > “$config_dir/main.chroot” << EOF
#!/bin/sh
{ set -e
apt install -y curl nfs-common xfsprogs ssh iptables
mkdir -p /mnt/block
mkdir -p /mnt/disk
mkdir /hooks
touch /hooks/boot.sh
chmod 777 /hooks/boot.sh
mkdir -p /home/$username/.ssh
chmod 700 /home/$username/.ssh
curl -o “/home/$username/.ssh/authorized_keys” “http://$ip_address:7777/authorized_keys”
chmod 600 /home/$username/.ssh/authorized_keys
. /usr/share/initramfs-tools/hook-functions
echo “Including /sbin/reboot in initramfs…”
copy_exec /sbin/reboot
update-initramfs -u
echo “PS1='[\033[01;32m]\u@\h:[\033[01;34m]\w[\033[00m]$ '” >> /etc/profile
echo “curl -sfL http://$ip_address:7777/boot-time.sh | sh -s -” > /hooks/boot.sh
script_path=“/hooks/boot.sh”
tmp_crontab_file=$(mktemp)
echo “@reboot $script_path” > “$tmp_crontab_file”
crontab “$tmp_crontab_file”
rm “$tmp_crontab_file”
echo “Cron job to run ‘$script_path’ at boot has been added.”
cat > /usr/local/bin/network-reboot.sh << ‘EOF_SCRIPT’
#!/bin/bash
nc -l -p $port
sleep 1
echo “ACK” | nc “$ip_address” “$port” -q 0
sleep 1
echo 1 > /proc/sys/kernel/sysrq
echo b > /proc/sysrq-trigger
EOF_SCRIPT
chmod +x /usr/local/bin/network-reboot.sh
cat > /etc/systemd/system/network-reboot.service << ‘SERVICE_EOF’
[Unit]
Description=Network-based Reboot Service
After=network.target

[Service]
ExecStart=/usr/local/bin/network-reboot.sh
Restart=always
User=root

[Install]
WantedBy=multi-user.target
SERVICE_EOF
systemctl enable network-reboot.service
} 2>&1 | tee -a /conf1
EOF
chmod +x “$config_dir/main.chroot”
echo “Made $config_dir/main.chroot”
echo “Edit this file for scripts that run during build time”
cat > “$config_dir/boot-time.sh” << EOF
#!/bin/sh
{ set -e
chown -R $username:$username /home/$username
MAC_ADDRESS=$(ip link | awk ‘/ether/ {print $2; exit}’)
sanitized_hostname=$(echo “$MAC_ADDRESS” | sed ‘s/[^a-zA-Z0-9.-]//g’)
sanitized_hostname=$(echo “$sanitized_hostname” | sed ‘s/^-//;s/-$//’)
if test -z “$sanitized_hostname”; then
echo “Error: Hostname is invalid after sanitization.”
else
hostnamectl set-hostname “$sanitized_hostname”; fi
echo “Hostname set to: $(hostnamectl status --static)”
dhclient -r
dhclient
mount -t nfs $ip_address:$netos_path/disks /mnt/block
IMAGE=“/mnt/block/${sanitized_hostname}.img”
if [ -f “$IMAGE” ]; then
rm “$IMAGE”; fi
fallocate -l 10G $IMAGE
mkfs.xfs $IMAGE
mount -o loop $IMAGE /mnt/disk
echo “mounted disk”
iptables -A INPUT -p tcp -s $ip --dport $port -j ACCEPT
iptables -A INPUT -p tcp --dport $port -j DROP
} 2>&1 | tee -a /conf2
EOF
chmod +x “$config_dir/boot-time.sh”
echo “Made $config_dir/boot-time.sh”
echo “Edit this file for scripts that run during boot time”
cat > “$config_dir/build.sh” << EOF
#!/bin/sh
{ set -e
cd “$config_dir”
cp -avx authorized_keys …/http/
cp -avx boot-time.sh …/http/
cp -avx lb-config …/build/auto/config
cp -avx default …/tftp/pxelinux.cfg/
cd …/build/
lb clean #–purge
rm -rf config/ local/
lb config
cp -avx “…/configs/main.chroot” config/hooks/
lb build
cd …/
rm -rf rootfs/*
cp -avx build/binary/* rootfs/
cp -v build/binary/live/initrd.img-* http/initrd.img
chmod o+rwx http/initrd.img
cp -v build/binary/live/vmlinuz-* http/vmlinuz
chmod o+rwx http/vmlinuz
} 2>&1 | tee -a build.log
EOF
chmod +x “$config_dir/build.sh”
echo “Made $config_dir/build.sh”
echo “Run this script to build the rootfs for clients”
section=“3”
echo “Creating Nginx configuration for $netos_path/http on port 7777…”
cat > “/etc/nginx/sites-available/netos” << EOF
server {
listen 7777;
server_name localhost;

location / {
    root $netos_path/http;
    autoindex on;
    try_files \$uri \$uri/ =404;
}

}
EOF
if [ ! -L “/etc/nginx/sites-enabled/netos” ]; then
ln -s “/etc/nginx/sites-available/netos” “/etc/nginx/sites-enabled/netos”; fi
echo “Testing Nginx configuration…”
if nginx -t; then
echo “Reloading Nginx…”
systemctl reload nginx
echo “Nginx is now serving files from $netos_path/http on port 7777.”
else
echo “Nginx configuration failed. Please check the syntax and try again.”
exit 1; fi
tftp_conf=“/etc/default/tftpd-hpa”
if [ ! -f “$tftp_conf.bak” ]; then
cp “$tftp_conf” “$tftp_conf.bak”
echo “Original TFTP configuration backed up at $tftp_conf.bak”; fi
echo “Configuring TFTP server to serve files from $netos_path/tftp…”
cat > “$tftp_conf” << EOF
TFTP_USERNAME=“tftp”
TFTP_DIRECTORY=“$netos_path/tftp”
TFTP_ADDRESS=“0.0.0.0:69”
TFTP_OPTIONS=“–secure”
EOF
chown -R tftp:tftp “$netos_path/tftp”
chmod -R 755 “$netos_path/tftp”
echo “Restarting TFTP service…”
systemctl restart tftpd-hpa
if systemctl is-active --quiet tftpd-hpa; then
echo “TFTP server is now serving files from $netos_path/tftp.”
else
echo “Failed to restart TFTP service. Please check the configuration.”
exit 1; fi
exports_file=“/etc/exports”
chmod 755 “$netos_path”
if [ ! -f “$exports_file.bak” ]; then
cp “$exports_file” “$exports_file.bak”
echo “Original NFS configuration backed up at $exports_file.bak”; fi
if grep -q “$netos_path” “$exports_file”; then
echo “$netos_path is already exported.”
else
echo “Adding $netos_path to NFS exports…”
echo “$netos_path *(rw,sync,no_subtree_check,no_root_squash)” >> “$exports_file”
echo “$netos_path has been added to NFS exports.”
echo “Applying NFS export changes…”
exportfs -a
echo “Restarting NFS server…”
systemctl restart nfs-kernel-server; fi
if systemctl is-active --quiet nfs-kernel-server; then
echo “NFS server is now serving $netos_path.”
else
echo “Failed to find NFS service. Please check the configuration.”
exit 1; fi
get_network_address() {
local ip=“$1”
local cidr
local network_address
cidr=$(ip -o -4 addr show | grep “$ip” | awk ‘{print $4}’)
if [ -z “$cidr” ]; then
echo “Error: Unable to find network information for IP $ip”
return 1; fi
network_address=$(ipcalc -n “$cidr” | grep Network | awk ‘{print $2}’)
echo “$network_address” | awk -F/ ‘{print $1}’; }
dnsmasq_conf=“/etc/dnsmasq.d/pxe.conf”
if [ -f “$dnsmasq_conf” ]; then
echo “dnsmasq is already configured for PXE Proxy DHCP.”; fi
echo “Configuring dnsmasq for PXE Proxy DHCP…”
cat > “$dnsmasq_conf” << EOF
port=0
dhcp-range=$(get_network_address “$ip”),proxy
log-dhcp
dhcp-boot=lpxelinux.0
pxe-service=x86PC,“Network Boot”,lpxelinux
EOF
echo “Restarting dnsmasq service…”
systemctl restart dnsmasq
if systemctl is-active --quiet dnsmasq; then
echo “dnsmasq is now serving as PXE Proxy DHCP.”
else
echo “Failed to restart dnsmasq. Please check the configuration.”
exit 1;fi
echo
echo “Made $config_dir/default”
echo “This file defines the PXE boot configuration. It specifies the kernel (vmlinuz) and initramfs (initrd.img) to be loaded over the network.”
echo “Edit this file to adjust boot parameters, such as adding additional kernel options or changing the initramfs URL.”
echo “You may also want to modify this if you need to change the NFS root location, modify IP configurations, or set up serial console settings.”
echo
echo “Made $config_dir/lb-config”
echo “This file configures the live-build process for creating a custom live environment.”
echo “You can edit it to change the Ubuntu distribution (e.g., from ‘jammy’ to ‘focal’), adjust repository mirrors, or add/remove package sources.”
echo “Modifying this file allows for changing system build modes or other system build configurations.”
echo
echo “Made $config_dir/authorized_keys”
echo “This file contains the SSH keys that will be added to all clients, allowing SSH access.”
echo “Edit this file to add or remove SSH public keys for users that will access the system.”
echo “Useful when managing SSH access for multiple users or machines.”
echo
echo “Made $config_dir/main.chroot”
echo “This script installs necessary packages and sets up the system during the build process.”
echo “You can edit this to add more packages or configurations needed during system creation (e.g., install custom software, set file permissions, or run custom scripts).”
echo “Typically, you modify this script if you need to change what happens inside the chroot environment, such as preparing hooks or configuring initramfs.”
echo
echo “Made $config_dir/boot-time.sh”
echo “This script is executed during system boot. It handles hostname configuration, network reinitialization, and mounting NFS shares.”
echo “You can edit this script to add tasks that should occur every time a system boots, like setting up networking, mounting additional storage, or applying specific host settings.”
echo “Changes to this file are useful for customizing tasks that should occur once the system is live, such as mounting remote filesystems or setting up loopback devices.”
echo
echo “Made $config_dir/build.sh”
echo “This script runs the full build process, assembling the root filesystem for the clients.”
echo “You can edit this to customize the build steps, such as including additional files or modifying how the build is cleaned up and packaged.”
echo “You’ll typically run this script when you want to generate the live system image, and you might adjust it if you want to change how files are copied or organized during the build.”
echo
echo “Made $config_dir/livemanage”
echo “This script allows you to SSH into or reboot each stateless system by selecting a host from a list.”
echo “It automatically lists hosts based on image files (.img) in the $netos_path/disks/ directory and network connection status.”
echo “Edit this file if you want to change how hosts are listed or to customize the SSH options.”
echo
while true; do
read -p "Do you want to run the build script? (y/n) [N]: " -r build
build=${build:-n}
if [ “$build” == “y” ]; then
echo “Starting build process.”
“$config_dir/build.sh”
elif [ “$build” == “n” ]; then
echo “Thank you, Goodbye.”
else
echo “Invalid option. Please type ‘y’ or ‘n’.”
continue; fi
break; done
trap - EXIT
} 2>&1 | tee -a build.log