aboutsummaryrefslogtreecommitdiff
path: root/custom/install-archzfs
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-17 23:15:33 -0600
committerCraig Jennings <c@cjennings.net>2026-01-17 23:15:33 -0600
commit2ebd6f7d76feed9d83a743b168fd148fe0eb8496 (patch)
tree8b3fd4db0661efa03ba9f1134c97322f17a9fd43 /custom/install-archzfs
parente655922db23c380ff8e62e4ffc8d09e82fe3d691 (diff)
downloadarchangel-2ebd6f7d76feed9d83a743b168fd148fe0eb8496.tar.gz
archangel-2ebd6f7d76feed9d83a743b168fd148fe0eb8496.zip
Add multi-disk RAID, SSH config, stripe option, remove colors
- Multi-disk RAID support: mirror, stripe, raidz1/2/3 - EFI partitions on all disks for boot redundancy - SSH configuration prompt (default yes) with sshd enabled - Stripe option for max capacity without redundancy - Genesis snapshot with rollback-to-genesis script - NetworkManager added to ISO for WiFi config - Remove color codes for better terminal compatibility - archsetup launcher via curl
Diffstat (limited to 'custom/install-archzfs')
-rwxr-xr-xcustom/install-archzfs999
1 files changed, 586 insertions, 413 deletions
diff --git a/custom/install-archzfs b/custom/install-archzfs
index 2afc9b6..733660b 100755
--- a/custom/install-archzfs
+++ b/custom/install-archzfs
@@ -4,75 +4,107 @@
#
# Installs Arch Linux on ZFS root with native encryption.
# Designed to be run from the custom archzfs ISO.
+#
+# Features:
+# - All questions asked upfront, then unattended installation
+# - Optional WiFi configuration with connection test
+# - ZFS native encryption (passphrase required at boot)
+# - Pre-pacman ZFS snapshots for safe upgrades
set -e
+#############################
+# Configuration
+#############################
+
# These will be set interactively
HOSTNAME=""
-USERNAME=""
TIMEZONE=""
LOCALE="en_US.UTF-8"
KEYMAP="us"
+ROOT_PASSWORD=""
+ZFS_PASSPHRASE=""
+WIFI_SSID=""
+WIFI_PASSWORD=""
# ZFS Configuration
POOL_NAME="zroot"
COMPRESSION="zstd"
ASHIFT="12" # 4K sectors (use 13 for 8K)
-# Colors
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-CYAN='\033[0;36m'
-BOLD='\033[1m'
-NC='\033[0m'
+# Multi-disk RAID support
+SELECTED_DISKS=() # Array of selected disk paths (/dev/sda, /dev/sdb, ...)
+ZFS_PARTS=() # Array of ZFS partition paths
+EFI_PARTS=() # Array of EFI partition paths
+RAID_LEVEL="" # "", "mirror", "raidz1", "raidz2", "raidz3"
+ENABLE_SSH="yes" # Enable SSH with root login (default yes for headless)
# Logging
LOGFILE="/tmp/install-archzfs.log"
exec > >(tee -a "$LOGFILE") 2>&1
-info() { echo -e "${GREEN}[INFO]${NC} $1"; }
-warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
-error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
-step() { echo -e "\n${BLUE}==>${NC} ${CYAN}$1${NC}"; }
-prompt() { echo -e "${BOLD}$1${NC}"; }
+info() { echo "[INFO] $1"; }
+warn() { echo "[WARN] $1"; }
+error() { echo "[ERROR] $1"; exit 1; }
+step() { echo ""; echo "==> $1"; }
+prompt() { echo "$1"; }
-# Check root
-[[ $EUID -ne 0 ]] && error "This script must be run as root"
+#############################
+# Pre-flight Checks
+#############################
-# Check ZFS module
-if ! lsmod | grep -q zfs; then
- info "Loading ZFS module..."
- modprobe zfs || error "Failed to load ZFS module"
-fi
+preflight_checks() {
+ # Check root
+ [[ $EUID -ne 0 ]] && error "This script must be run as root"
+
+ # Check ZFS module
+ if ! lsmod | grep -q zfs; then
+ info "Loading ZFS module..."
+ modprobe zfs || error "Failed to load ZFS module. Is zfs-linux-lts installed?"
+ fi
+
+ info "ZFS module loaded successfully."
+}
+
+#############################
+# Phase 1: Gather All Input
+#############################
-### Interactive Configuration ###
-configure_install() {
- step "Installation Configuration"
+gather_input() {
+ echo ""
+ echo "╔═══════════════════════════════════════════════════════════════╗"
+ echo "║ Arch Linux ZFS Root ║"
+ echo "║ Configuration and Installation ║"
+ echo "╚═══════════════════════════════════════════════════════════════╝"
+ echo ""
+ info "Answer all questions now. Installation will run unattended afterward."
echo ""
- # Hostname
+ get_hostname
+ get_timezone
+ get_locale
+ get_keymap
+ get_disks
+ get_raid_level
+ get_wifi
+ get_zfs_passphrase
+ get_root_password
+ get_ssh_config
+ show_summary
+}
+
+get_hostname() {
+ step "Hostname"
prompt "Enter hostname for this system:"
read -p "> " HOSTNAME
while [[ -z "$HOSTNAME" || ! "$HOSTNAME" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$ ]]; do
warn "Invalid hostname. Use letters, numbers, and hyphens (no spaces)."
read -p "> " HOSTNAME
done
+}
- echo ""
-
- # Username
- prompt "Enter primary username:"
- read -p "> " USERNAME
- while [[ -z "$USERNAME" || ! "$USERNAME" =~ ^[a-z_][a-z0-9_-]*$ ]]; do
- warn "Invalid username. Use lowercase letters, numbers, underscore, hyphen."
- read -p "> " USERNAME
- done
-
- echo ""
-
- # Timezone selection
+get_timezone() {
+ step "Timezone"
prompt "Select timezone region:"
PS3="Region: "
select region in "America" "Europe" "Asia" "Australia" "Pacific" "Other"; do
@@ -87,7 +119,6 @@ configure_install() {
else
echo ""
prompt "Select city:"
- # List cities for selected region
mapfile -t cities < <(find /usr/share/zoneinfo/"$region" -maxdepth 1 -type f -printf '%f\n' | sort)
PS3="City: "
select city in "${cities[@]}"; do
@@ -97,10 +128,10 @@ configure_install() {
fi
done
fi
+}
- echo ""
-
- # Locale selection
+get_locale() {
+ step "Locale"
prompt "Select locale:"
PS3="Locale: "
select loc in "en_US.UTF-8" "en_GB.UTF-8" "de_DE.UTF-8" "fr_FR.UTF-8" "es_ES.UTF-8" "Other"; do
@@ -114,10 +145,10 @@ configure_install() {
break
fi
done
+}
- echo ""
-
- # Keymap selection
+get_keymap() {
+ step "Keyboard Layout"
prompt "Select keyboard layout:"
PS3="Keymap: "
select km in "us" "uk" "de" "fr" "es" "dvorak" "Other"; do
@@ -131,126 +162,325 @@ configure_install() {
break
fi
done
+}
- # Confirm settings
+get_disks() {
+ step "Disk Selection"
echo ""
- echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- echo -e "${BOLD}Configuration Summary:${NC}"
- echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- echo " Hostname: $HOSTNAME"
- echo " Username: $USERNAME"
- echo " Timezone: $TIMEZONE"
- echo " Locale: $LOCALE"
- echo " Keymap: $KEYMAP"
- echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo "Available disks:"
+ echo "----------------"
+ lsblk -d -o NAME,SIZE,MODEL,TYPE | grep disk
echo ""
- read -p "Is this correct? [Y/n]: " confirm
- if [[ "$confirm" == "n" || "$confirm" == "N" ]]; then
- configure_install
+ # Get list of available disks
+ mapfile -t AVAILABLE_DISKS < <(lsblk -d -n -o NAME,TYPE | awk '$2=="disk"{print $1}')
+
+ if [[ ${#AVAILABLE_DISKS[@]} -eq 0 ]]; then
+ error "No disks found!"
+ fi
+
+ # Build dialog checklist items
+ local dialog_items=()
+ for disk in "${AVAILABLE_DISKS[@]}"; do
+ local size=$(lsblk -d -n -o SIZE "/dev/$disk" | tr -d ' ')
+ local model=$(lsblk -d -n -o MODEL "/dev/$disk" | tr -d ' ' | head -c 20)
+ dialog_items+=("$disk" "$size $model" "off")
+ done
+
+ # Use dialog for multi-select
+ local result
+ result=$(dialog --stdout --checklist "Select disks for installation (SPACE to select, ENTER to confirm):" \
+ 20 70 10 "${dialog_items[@]}") || error "Disk selection cancelled"
+
+ if [[ -z "$result" ]]; then
+ error "No disks selected!"
+ fi
+
+ # Parse selected disks
+ SELECTED_DISKS=()
+ for disk in $result; do
+ SELECTED_DISKS+=("/dev/$disk")
+ done
+
+ clear
+ echo ""
+ warn "Selected ${#SELECTED_DISKS[@]} disk(s):"
+ for disk in "${SELECTED_DISKS[@]}"; do
+ echo " - $disk"
+ lsblk "$disk" | sed 's/^/ /'
+ done
+ echo ""
+
+ read -p "This will DESTROY all data on these disks. Type 'yes' to continue: " confirm
+ if [[ "$confirm" != "yes" ]]; then
+ error "Aborted by user"
fi
}
-### Disk Selection ###
-select_disk() {
- step "Disk Selection"
+get_raid_level() {
+ local disk_count=${#SELECTED_DISKS[@]}
+
+ if [[ $disk_count -eq 1 ]]; then
+ RAID_LEVEL=""
+ info "Single disk selected - no RAID"
+ return
+ fi
+ step "RAID Configuration"
echo ""
- echo "Available disks:"
- echo "----------------"
- lsblk -d -o NAME,SIZE,MODEL,TYPE | grep disk
+ echo "You have selected $disk_count disks."
echo ""
- # Get list of disks
- mapfile -t DISKS < <(lsblk -d -n -o NAME,TYPE | awk '$2=="disk"{print $1}')
+ # Build options based on disk count
+ local options=("mirror" "stripe")
+ local descriptions=(
+ "mirror - All disks mirror each other (max redundancy, ${disk_count}x durability)"
+ "stripe - Combine disks for max capacity (NO redundancy, ${disk_count}x space)"
+ )
- if [[ ${#DISKS[@]} -eq 0 ]]; then
- error "No disks found!"
+ if [[ $disk_count -ge 3 ]]; then
+ options+=("raidz1")
+ descriptions+=("raidz1 - Single parity (can lose 1 disk)")
+ fi
+ if [[ $disk_count -ge 4 ]]; then
+ options+=("raidz2")
+ descriptions+=("raidz2 - Double parity (can lose 2 disks)")
+ fi
+ if [[ $disk_count -ge 5 ]]; then
+ options+=("raidz3")
+ descriptions+=("raidz3 - Triple parity (can lose 3 disks)")
fi
- PS3="Select disk for installation (number): "
- select disk in "${DISKS[@]}"; do
- if [[ -n "$disk" ]]; then
- DISK="/dev/$disk"
+ echo "Available RAID levels:"
+ for i in "${!descriptions[@]}"; do
+ echo " $((i+1))) ${descriptions[$i]}"
+ done
+ echo ""
+
+ PS3="Select RAID level: "
+ select level in "${options[@]}"; do
+ if [[ -n "$level" ]]; then
+ RAID_LEVEL="$level"
break
fi
done
+ info "RAID level: $RAID_LEVEL"
+}
+
+get_wifi() {
+ step "WiFi Configuration (Optional)"
echo ""
- warn "Selected disk: $DISK"
+ prompt "Do you want to configure WiFi? [y/N]:"
+ read -p "> " configure_wifi
+
+ if [[ "$configure_wifi" =~ ^[Yy]$ ]]; then
+ # Ensure NetworkManager is running
+ systemctl start NetworkManager 2>/dev/null || true
+ sleep 2
+
+ echo ""
+ info "Scanning for networks..."
+ nmcli device wifi rescan 2>/dev/null || true
+ sleep 2
+ echo ""
+ echo "Available networks:"
+ nmcli device wifi list
+ echo ""
+
+ prompt "Enter WiFi SSID:"
+ read -p "> " WIFI_SSID
+
+ prompt "Enter WiFi password:"
+ read -s -p "> " WIFI_PASSWORD
+ echo ""
+
+ # Test the connection
+ info "Testing WiFi connection..."
+ if nmcli device wifi connect "$WIFI_SSID" password "$WIFI_PASSWORD" 2>/dev/null; then
+ info "WiFi connection successful!"
+ else
+ warn "WiFi connection failed. You can configure it manually after installation."
+ WIFI_SSID=""
+ WIFI_PASSWORD=""
+ fi
+ else
+ info "Skipping WiFi configuration."
+ fi
+}
+
+get_zfs_passphrase() {
+ step "ZFS Encryption Passphrase"
+ echo ""
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "This passphrase will be required at EVERY boot."
+ echo ""
+ echo "Requirements:"
+ echo " - Use a strong, memorable passphrase"
+ echo " - If forgotten, your data is UNRECOVERABLE"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
- lsblk "$DISK"
+
+ while true; do
+ prompt "Enter ZFS encryption passphrase:"
+ read -s -p "> " ZFS_PASSPHRASE
+ echo ""
+
+ prompt "Confirm passphrase:"
+ read -s -p "> " confirm_pass
+ echo ""
+
+ if [[ "$ZFS_PASSPHRASE" == "$confirm_pass" ]]; then
+ if [[ ${#ZFS_PASSPHRASE} -lt 8 ]]; then
+ warn "Passphrase should be at least 8 characters."
+ continue
+ fi
+ break
+ else
+ warn "Passphrases do not match. Try again."
+ fi
+ done
+}
+
+get_root_password() {
+ step "Root Password"
echo ""
- read -p "This will DESTROY all data on $DISK. Type 'yes' to continue: " confirm
- [[ "$confirm" != "yes" ]] && error "Aborted by user"
+ while true; do
+ prompt "Enter root password:"
+ read -s -p "> " ROOT_PASSWORD
+ echo ""
+
+ prompt "Confirm root password:"
+ read -s -p "> " confirm_pass
+ echo ""
+
+ if [[ "$ROOT_PASSWORD" == "$confirm_pass" ]]; then
+ break
+ else
+ warn "Passwords do not match. Try again."
+ fi
+ done
}
-### Partitioning ###
-partition_disk() {
- step "Partitioning $DISK"
-
- # Wipe existing signatures
- info "Wiping existing signatures..."
- wipefs -af "$DISK"
- sgdisk --zap-all "$DISK"
-
- # Create partitions
- # 1: EFI System Partition (1GB)
- # 2: ZFS partition (rest)
- info "Creating partitions..."
- sgdisk -n 1:0:+1G -t 1:ef00 -c 1:"EFI" "$DISK"
- sgdisk -n 2:0:0 -t 2:bf00 -c 2:"ZFS" "$DISK"
-
- # Determine partition names (handle nvme vs sda naming)
- if [[ "$DISK" == *"nvme"* ]] || [[ "$DISK" == *"mmcblk"* ]]; then
- EFI_PART="${DISK}p1"
- ZFS_PART="${DISK}p2"
+get_ssh_config() {
+ step "SSH Configuration"
+ echo ""
+ info "SSH enables remote access after installation."
+ info "Recommended for headless servers. Harden with archsetup later."
+ echo ""
+ prompt "Enable SSH with root login? [Y/n]:"
+ read -p "> " ssh_choice
+
+ if [[ "$ssh_choice" =~ ^[Nn]$ ]]; then
+ ENABLE_SSH="no"
+ info "SSH will not be enabled."
else
- EFI_PART="${DISK}1"
- ZFS_PART="${DISK}2"
+ ENABLE_SSH="yes"
+ info "SSH will be enabled with root password login."
+ warn "Remember to harden SSH (key auth, fail2ban) with archsetup!"
fi
+}
+
+show_summary() {
+ echo ""
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "Configuration Summary:"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo " Hostname: $HOSTNAME"
+ echo " Timezone: $TIMEZONE"
+ echo " Locale: $LOCALE"
+ echo " Keymap: $KEYMAP"
+ echo " Disks: ${#SELECTED_DISKS[@]} disk(s)"
+ for disk in "${SELECTED_DISKS[@]}"; do
+ local size=$(lsblk -d -n -o SIZE "$disk" | tr -d ' ')
+ echo " - $disk ($size)"
+ done
+ echo " RAID Level: ${RAID_LEVEL:-single (no RAID)}"
+ echo " WiFi: ${WIFI_SSID:-Not configured}"
+ echo " SSH: ${ENABLE_SSH:-yes} (root login)"
+ echo " ZFS Pool: $POOL_NAME (encrypted)"
+ echo " Boot: EFI on all disks (redundant)"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo ""
+
+ read -p "Press Enter to begin installation, or Ctrl+C to abort..."
+}
+
+#############################
+# Phase 2: Installation
+#############################
+
+partition_disks() {
+ step "Partitioning ${#SELECTED_DISKS[@]} disk(s)"
+
+ EFI_PARTS=()
+ ZFS_PARTS=()
+
+ for disk in "${SELECTED_DISKS[@]}"; do
+ info "Partitioning $disk..."
+
+ # Wipe existing signatures
+ wipefs -af "$disk"
+ sgdisk --zap-all "$disk"
+
+ # Create partitions: 1G EFI + rest for ZFS
+ sgdisk -n 1:0:+1G -t 1:ef00 -c 1:"EFI" "$disk"
+ sgdisk -n 2:0:0 -t 2:bf00 -c 2:"ZFS" "$disk"
+
+ # Determine partition names (handle nvme/mmcblk naming)
+ local efi_part zfs_part
+ if [[ "$disk" == *"nvme"* ]] || [[ "$disk" == *"mmcblk"* ]]; then
+ efi_part="${disk}p1"
+ zfs_part="${disk}p2"
+ else
+ efi_part="${disk}1"
+ zfs_part="${disk}2"
+ fi
+
+ EFI_PARTS+=("$efi_part")
+ ZFS_PARTS+=("$zfs_part")
+
+ sleep 1
+ partprobe "$disk"
+ done
- # Wait for partitions to appear
- sleep 2
- partprobe "$DISK"
sleep 2
- # Format EFI partition
- info "Formatting EFI partition..."
- mkfs.fat -F32 -n EFI "$EFI_PART"
+ # Format all EFI partitions
+ for i in "${!EFI_PARTS[@]}"; do
+ info "Formatting EFI partition ${EFI_PARTS[$i]}..."
+ mkfs.fat -F32 -n "EFI$i" "${EFI_PARTS[$i]}"
+ done
- info "Partitioning complete."
- lsblk "$DISK"
+ info "Partitioning complete. Created ${#EFI_PARTS[@]} EFI and ${#ZFS_PARTS[@]} ZFS partitions."
}
-### ZFS Pool Creation ###
create_zfs_pool() {
step "Creating ZFS Pool with Native Encryption"
- # Check if pool already exists
if zpool list "$POOL_NAME" &>/dev/null; then
warn "Pool $POOL_NAME already exists. Destroying..."
zpool destroy -f "$POOL_NAME"
fi
- echo ""
- echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- echo -e "${BOLD}ZFS Encryption Passphrase${NC}"
- echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- echo ""
- echo "You will now create an encryption passphrase."
- echo "This passphrase will be required at EVERY boot."
- echo ""
- echo "Requirements:"
- echo " - Use a strong, memorable passphrase"
- echo " - If forgotten, your data is UNRECOVERABLE"
- echo ""
- echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- echo ""
+ # Build pool configuration based on RAID level
+ local pool_config
+ if [[ "$RAID_LEVEL" == "stripe" ]]; then
+ # Stripe: just list devices without a vdev type (RAID0 equivalent)
+ pool_config="${ZFS_PARTS[*]}"
+ info "Creating striped pool with ${#ZFS_PARTS[@]} disks (NO redundancy)..."
+ warn "Data loss will occur if ANY disk fails!"
+ elif [[ -n "$RAID_LEVEL" ]]; then
+ pool_config="$RAID_LEVEL ${ZFS_PARTS[*]}"
+ info "Creating $RAID_LEVEL pool with ${#ZFS_PARTS[@]} disks..."
+ else
+ pool_config="${ZFS_PARTS[0]}"
+ info "Creating single-disk pool..."
+ fi
- # Create encrypted pool
- zpool create -f \
+ # Create encrypted pool using passphrase from variable
+ echo "$ZFS_PASSPHRASE" | zpool create -f \
-o ashift="$ASHIFT" \
-o autotrim=on \
-O acltype=posixacl \
@@ -266,33 +496,37 @@ create_zfs_pool() {
-O keylocation=prompt \
-O mountpoint=none \
-R /mnt \
- "$POOL_NAME" "$ZFS_PART"
+ "$POOL_NAME" $pool_config
info "ZFS pool created successfully."
+ zpool status "$POOL_NAME"
}
-### Dataset Creation ###
create_datasets() {
step "Creating ZFS Datasets"
# Root dataset container
zfs create -o mountpoint=none -o canmount=off "$POOL_NAME/ROOT"
- # Main root filesystem with reservation for safety
- zfs create -o mountpoint=/ -o canmount=noauto -o reservation=50G "$POOL_NAME/ROOT/default"
+ # Main root filesystem
+ # Reserve 20% of pool or 20G max to prevent pool from filling completely
+ local pool_size_bytes=$(zpool get -Hp size "$POOL_NAME" | awk '{print $3}')
+ local pool_size_gb=$((pool_size_bytes / 1024 / 1024 / 1024))
+ local reserve_gb=$((pool_size_gb / 5)) # 20%
+ [[ $reserve_gb -gt 20 ]] && reserve_gb=20
+ [[ $reserve_gb -lt 5 ]] && reserve_gb=5
- # Mount root first
+ zfs create -o mountpoint=/ -o canmount=noauto -o reservation=${reserve_gb}G "$POOL_NAME/ROOT/default"
zfs mount "$POOL_NAME/ROOT/default"
- # Home datasets
+ # Home (archsetup will create user subdataset)
zfs create -o mountpoint=/home "$POOL_NAME/home"
zfs create -o mountpoint=/root "$POOL_NAME/home/root"
- zfs create -o mountpoint="/home/$USERNAME" "$POOL_NAME/home/$USERNAME"
- # Media dataset - compression off for already-compressed files
+ # Media - compression off for already-compressed files
zfs create -o mountpoint=/media -o compression=off "$POOL_NAME/media"
- # VMs dataset - larger recordsize for VM disk images
+ # VMs - 64K recordsize for VM disk images
zfs create -o mountpoint=/vms -o recordsize=64K "$POOL_NAME/vms"
# Var datasets
@@ -303,46 +537,56 @@ create_datasets() {
zfs create -o mountpoint=/var/lib/pacman "$POOL_NAME/var/lib/pacman"
zfs create -o mountpoint=/var/lib/docker "$POOL_NAME/var/lib/docker"
- # Exclude temp directories from snapshots
+ # Temp directories - excluded from snapshots
zfs create -o mountpoint=/var/tmp -o com.sun:auto-snapshot=false "$POOL_NAME/var/tmp"
zfs create -o mountpoint=/tmp -o com.sun:auto-snapshot=false "$POOL_NAME/tmp"
chmod 1777 /mnt/tmp /mnt/var/tmp
info "Datasets created:"
- echo ""
- zfs list -r "$POOL_NAME" -o name,mountpoint,compression,reservation
+ zfs list -r "$POOL_NAME" -o name,mountpoint,compression
}
-### Mount EFI ###
mount_efi() {
step "Mounting EFI Partition"
-
mkdir -p /mnt/boot
- mount "$EFI_PART" /mnt/boot
-
- info "EFI partition mounted at /mnt/boot"
+ # Mount primary (first) EFI partition
+ mount "${EFI_PARTS[0]}" /mnt/boot
+ info "Primary EFI partition ${EFI_PARTS[0]} mounted at /mnt/boot"
}
-### Install Base System ###
install_base() {
step "Installing Base System"
info "Updating pacman keys..."
pacman-key --init
pacman-key --populate archlinux
+
+ # Add archzfs key
pacman-key -r DDF7DB817396A49B2A2723F7403BD972F75D9D76 2>/dev/null || true
pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76 2>/dev/null || true
+ # Add archzfs repo to pacman.conf for pacstrap
+ if ! grep -q "\[archzfs\]" /etc/pacman.conf; then
+ cat >> /etc/pacman.conf << 'EOF'
+
+[archzfs]
+Server = https://archzfs.com/$repo/$arch
+SigLevel = Optional TrustAll
+EOF
+ fi
+
info "Installing base packages (this takes a while)..."
+ info "ZFS will be built from source via DKMS - this ensures kernel compatibility."
pacstrap -K /mnt \
base \
base-devel \
- linux \
- linux-headers \
+ linux-lts \
+ linux-lts-headers \
linux-firmware \
- zfs-linux \
+ zfs-dkms \
zfs-utils \
grub \
+ freetype2 \
efibootmgr \
networkmanager \
openssh \
@@ -352,16 +596,15 @@ install_base() {
zsh \
nodejs \
npm \
- sanoid
+ ttf-dejavu
info "Base system installed."
}
-### Configure System ###
configure_system() {
step "Configuring System"
- # Generate fstab (only for EFI, ZFS handles the rest)
+ # fstab (only for EFI)
info "Generating fstab..."
echo "# /boot - EFI System Partition" > /mnt/etc/fstab
echo "UUID=$(blkid -s UUID -o value "$EFI_PART") /boot vfat defaults,noatime 0 2" >> /mnt/etc/fstab
@@ -389,7 +632,7 @@ configure_system() {
127.0.1.1 $HOSTNAME.localdomain $HOSTNAME
EOF
- # Add archzfs repo to installed system
+ # Add archzfs repo
info "Adding archzfs repository..."
cat >> /mnt/etc/pacman.conf << 'EOF'
@@ -398,27 +641,60 @@ Server = https://archzfs.com/$repo/$arch
SigLevel = Optional TrustAll
EOF
- # Import archzfs key in chroot
+ # Import archzfs key
arch-chroot /mnt pacman-key -r DDF7DB817396A49B2A2723F7403BD972F75D9D76 2>/dev/null || true
arch-chroot /mnt pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76 2>/dev/null || true
+
+ # Set root password
+ info "Setting root password..."
+ echo "root:$ROOT_PASSWORD" | arch-chroot /mnt chpasswd
+}
+
+configure_wifi() {
+ if [[ -n "$WIFI_SSID" ]]; then
+ step "Configuring WiFi"
+
+ # Copy NetworkManager connection from live environment
+ if [[ -d /etc/NetworkManager/system-connections ]]; then
+ mkdir -p /mnt/etc/NetworkManager/system-connections
+ cp /etc/NetworkManager/system-connections/* /mnt/etc/NetworkManager/system-connections/ 2>/dev/null || true
+ chmod 600 /mnt/etc/NetworkManager/system-connections/* 2>/dev/null || true
+ fi
+
+ info "WiFi configuration copied to installed system."
+ fi
+}
+
+configure_ssh() {
+ if [[ "$ENABLE_SSH" == "yes" ]]; then
+ step "Configuring SSH"
+
+ # Ensure sshd config allows root login with password
+ sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /mnt/etc/ssh/sshd_config
+ sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' /mnt/etc/ssh/sshd_config
+
+ # Enable sshd service
+ arch-chroot /mnt systemctl enable sshd
+
+ info "SSH enabled with root password login."
+ warn "Run archsetup to harden SSH (key auth, fail2ban)."
+ else
+ info "SSH not enabled. Enable manually if needed."
+ fi
}
-### Configure mkinitcpio ###
configure_initramfs() {
step "Configuring Initramfs for ZFS"
- # Backup original
cp /mnt/etc/mkinitcpio.conf /mnt/etc/mkinitcpio.conf.bak
# Configure hooks for ZFS
- # Order matters: keyboard before zfs for passphrase entry
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block zfs filesystems fsck)/' /mnt/etc/mkinitcpio.conf
info "Regenerating initramfs..."
arch-chroot /mnt mkinitcpio -P
}
-### Configure Bootloader ###
configure_bootloader() {
step "Configuring GRUB Bootloader"
@@ -434,20 +710,49 @@ GRUB_TERMINAL_OUTPUT="console"
GRUB_DISABLE_OS_PROBER=true
GRUB_GFXMODE=auto
GRUB_GFXPAYLOAD_LINUX=keep
+GRUB_FONT=/boot/grub/fonts/DejaVuSansMono32.pf2
EOF
- info "Installing GRUB..."
- arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB
+ # Install GRUB to each EFI partition for boot redundancy
+ info "Installing GRUB to ${#EFI_PARTS[@]} EFI partition(s)..."
+
+ for i in "${!EFI_PARTS[@]}"; do
+ local efi_part="${EFI_PARTS[$i]}"
+ local bootloader_id="GRUB"
+ if [[ ${#EFI_PARTS[@]} -gt 1 ]]; then
+ bootloader_id="GRUB-disk$((i+1))"
+ fi
+
+ # Unmount current boot if mounted, mount this EFI partition
+ umount /mnt/boot 2>/dev/null || true
+ mount "$efi_part" /mnt/boot
+
+ # Create directories and font
+ mkdir -p /mnt/boot/grub/fonts
+ arch-chroot /mnt grub-mkfont -s 32 -o /boot/grub/fonts/DejaVuSansMono32.pf2 \
+ /usr/share/fonts/TTF/DejaVuSansMono.ttf 2>/dev/null || true
- info "Generating GRUB configuration..."
- arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg
+ # Install GRUB
+ info "Installing GRUB to $efi_part (bootloader-id: $bootloader_id)..."
+ arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/boot \
+ --bootloader-id="$bootloader_id" --recheck
+
+ # Generate configuration
+ arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg
+ done
+
+ # Remount primary EFI for rest of installation
+ umount /mnt/boot 2>/dev/null || true
+ mount "${EFI_PARTS[0]}" /mnt/boot
+
+ if [[ ${#EFI_PARTS[@]} -gt 1 ]]; then
+ info "GRUB installed to all ${#EFI_PARTS[@]} disks for boot redundancy."
+ fi
}
-### Configure ZFS Services ###
configure_zfs_services() {
step "Configuring ZFS Services"
- # Enable ZFS services
arch-chroot /mnt systemctl enable zfs.target
arch-chroot /mnt systemctl enable zfs-import-cache
arch-chroot /mnt systemctl enable zfs-mount
@@ -458,7 +763,7 @@ configure_zfs_services() {
zpool set cachefile=/etc/zfs/zpool.cache "$POOL_NAME"
cp /etc/zfs/zpool.cache /mnt/etc/zfs/
- # Set bootfs property
+ # Set bootfs
zpool set bootfs="$POOL_NAME/ROOT/default" "$POOL_NAME"
# Enable other services
@@ -468,93 +773,8 @@ configure_zfs_services() {
info "ZFS services configured."
}
-### Configure Sanoid (Snapshot Management) ###
-configure_sanoid() {
- step "Configuring Sanoid Snapshot Management"
-
- mkdir -p /mnt/etc/sanoid
-
- cat > /mnt/etc/sanoid/sanoid.conf << EOF
-# Sanoid configuration for ZFS snapshots
-# https://github.com/jimsalterjrs/sanoid
-
-#############################
-# Templates
-#############################
-
-[template_production]
- # Frequent snapshots for active data
- hourly = 24
- daily = 7
- weekly = 4
- monthly = 12
- yearly = 0
- autosnap = yes
- autoprune = yes
-
-[template_backup]
- # Less frequent for large/static data
- hourly = 0
- daily = 7
- weekly = 4
- monthly = 6
- yearly = 0
- autosnap = yes
- autoprune = yes
-
-[template_none]
- # No automatic snapshots (for tmp, cache)
- autosnap = no
- autoprune = yes
-
-#############################
-# Datasets
-#############################
-
-# Root filesystem
-[$POOL_NAME/ROOT/default]
- use_template = production
-
-# Home directories
-[$POOL_NAME/home]
- use_template = production
- recursive = yes
-
-# Media (large files, less frequent snapshots)
-[$POOL_NAME/media]
- use_template = backup
-
-# VMs (snapshot before changes manually, or less frequently)
-[$POOL_NAME/vms]
- use_template = backup
-
-# Var data
-[$POOL_NAME/var/log]
- use_template = production
-
-[$POOL_NAME/var/lib/pacman]
- use_template = production
-
-# No snapshots for cache/tmp (handled by dataset property, but explicit here)
-[$POOL_NAME/var/cache]
- use_template = none
-
-[$POOL_NAME/var/tmp]
- use_template = none
-
-[$POOL_NAME/tmp]
- use_template = none
-EOF
-
- # Enable sanoid timer
- arch-chroot /mnt systemctl enable sanoid.timer
-
- info "Sanoid configured. Snapshots will run automatically."
-}
-
-### Configure Pacman ZFS Snapshot Hook ###
configure_pacman_hook() {
- step "Configuring Pacman Pre-Upgrade Snapshot Hook"
+ step "Configuring Pacman Snapshot Hook"
mkdir -p /mnt/etc/pacman.d/hooks
@@ -574,15 +794,11 @@ EOF
cat > /mnt/usr/local/bin/zfs-pre-snapshot << 'EOF'
#!/bin/bash
-# Create a ZFS snapshot before pacman transactions
-# This allows easy rollback if an upgrade breaks something
-
POOL="zroot"
DATASET="$POOL/ROOT/default"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
SNAPSHOT_NAME="pre-pacman_$TIMESTAMP"
-# Create the snapshot
if zfs snapshot "$DATASET@$SNAPSHOT_NAME"; then
echo "Created snapshot: $DATASET@$SNAPSHOT_NAME"
else
@@ -592,152 +808,121 @@ EOF
chmod +x /mnt/usr/local/bin/zfs-pre-snapshot
- info "Pacman hook configured. Snapshots will be created before each transaction."
+ info "Pacman hook configured."
}
-### Create User ###
-create_user() {
- step "Creating User: $USERNAME"
+copy_archsetup() {
+ step "Installing archsetup Launcher"
- arch-chroot /mnt useradd -m -G wheel -s /bin/zsh "$USERNAME" 2>/dev/null || \
- warn "User $USERNAME may already exist"
+ cat > /mnt/usr/local/bin/archsetup << 'EOF'
+#!/bin/bash
+curl -fsSL https://cjennings.net/archsetup | bash
+EOF
+ chmod +x /mnt/usr/local/bin/archsetup
+ info "archsetup launcher installed to /usr/local/bin/archsetup"
+}
- # Set ownership of home dataset
- arch-chroot /mnt chown -R "$USERNAME:$USERNAME" "/home/$USERNAME"
+sync_efi_partitions() {
+ # Skip if only one disk
+ if [[ ${#EFI_PARTS[@]} -le 1 ]]; then
+ return
+ fi
- # Configure sudo
- echo "%wheel ALL=(ALL:ALL) ALL" > /mnt/etc/sudoers.d/wheel
- chmod 440 /mnt/etc/sudoers.d/wheel
+ step "Syncing EFI Partitions for Redundancy"
- echo ""
- echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- echo -e "${BOLD}Set User Password${NC}"
- echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
- info "Set password for $USERNAME:"
- arch-chroot /mnt passwd "$USERNAME"
+ local primary_efi="${EFI_PARTS[0]}"
+ local temp_mount="/mnt/efi_sync"
- echo ""
- info "Set password for root:"
- arch-chroot /mnt passwd
-}
+ for i in "${!EFI_PARTS[@]}"; do
+ if [[ $i -eq 0 ]]; then
+ continue # Skip primary
+ fi
-### Copy archsetup ###
-copy_archsetup() {
- step "Copying archsetup to New System"
+ local efi_part="${EFI_PARTS[$i]}"
+ info "Syncing to EFI partition $((i+1)): $efi_part"
- if [[ -d /code/archsetup ]]; then
- mkdir -p "/mnt/home/$USERNAME/code"
- cp -r /code/archsetup "/mnt/home/$USERNAME/code/"
- arch-chroot /mnt chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/code"
- info "archsetup copied to /home/$USERNAME/code/archsetup"
- else
- warn "archsetup not found in ISO, skipping..."
- fi
+ mkdir -p "$temp_mount"
+ mount "$efi_part" "$temp_mount"
+
+ # Sync all content from primary EFI (mounted at /mnt/boot) to secondary
+ rsync -a --delete /mnt/boot/ "$temp_mount/"
+
+ umount "$temp_mount"
+ done
+
+ rmdir "$temp_mount" 2>/dev/null || true
+ info "All EFI partitions synchronized."
}
-### Create Syncoid Script for TrueNAS ###
-create_syncoid_script() {
- step "Creating Syncoid Replication Script"
+create_genesis_snapshot() {
+ step "Creating Genesis Snapshot"
- cat > /mnt/usr/local/bin/zfs-replicate << 'SCRIPT'
+ # Create recursive snapshot of entire pool
+ info "Creating snapshot ${POOL_NAME}@genesis..."
+ zfs snapshot -r "${POOL_NAME}@genesis"
+
+ # Create rollback script in /root
+ info "Installing rollback-to-genesis script..."
+ cat > /mnt/root/rollback-to-genesis << 'ROLLBACK_EOF'
#!/bin/bash
-# zfs-replicate - Replicate ZFS datasets to TrueNAS
-# Usage: zfs-replicate [dataset] [target]
+# rollback-to-genesis - Roll back all datasets to the genesis snapshot
#
-# Examples:
-# zfs-replicate # Replicate all configured datasets
-# zfs-replicate zroot/home user@truenas:/tank/backup/laptop
+# This script rolls back the entire ZFS pool to its pristine post-install state.
+# WARNING: This will destroy all changes made since installation!
set -e
-# Configuration - edit these for your TrueNAS setup
-TRUENAS_HOST="truenas" # TrueNAS hostname or IP
-TRUENAS_USER="root" # User with ZFS permissions
-TRUENAS_POOL="tank" # Destination pool
-BACKUP_PATH="backup/laptop" # Path under the pool
-
-# Datasets to replicate (space-separated)
-DATASETS="zroot/ROOT/default zroot/home zroot/media zroot/vms"
-
-# Colors
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-RED='\033[0;31m'
-NC='\033[0m'
-
-info() { echo -e "${GREEN}[INFO]${NC} $1"; }
-warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
-error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
-
-# Check if syncoid is installed
-command -v syncoid >/dev/null 2>&1 || error "syncoid not found. Install sanoid package."
-
-# Single dataset mode
-if [[ -n "$1" ]] && [[ -n "$2" ]]; then
- info "Replicating $1 to $2"
- syncoid --recursive "$1" "$2"
- exit 0
-fi
+POOL_NAME="zroot"
-# Full replication mode
-info "Starting ZFS replication to $TRUENAS_HOST"
+echo "╔═══════════════════════════════════════════════════════════════╗"
+echo "║ WARNING: Full System Rollback ║"
+echo "╚═══════════════════════════════════════════════════════════════╝"
+echo ""
+echo "This will roll back ALL datasets to the genesis snapshot!"
+echo "All changes since installation will be permanently lost."
echo ""
-for dataset in $DATASETS; do
- dest="$TRUENAS_USER@$TRUENAS_HOST:$TRUENAS_POOL/$BACKUP_PATH/${dataset#zroot/}"
- info "Replicating $dataset -> $dest"
-
- if syncoid --recursive "$dataset" "$dest"; then
- info " Success"
- else
- warn " Failed (will retry next run)"
- fi
- echo ""
+# Show what will be rolled back
+echo "Datasets to roll back:"
+zfs list -r -t snapshot -o name "${POOL_NAME}" 2>/dev/null | grep "@genesis" | while read snap; do
+ dataset="${snap%@genesis}"
+ echo " - $dataset"
done
+echo ""
-info "Replication complete."
-SCRIPT
-
- chmod +x /mnt/usr/local/bin/zfs-replicate
-
- # Create systemd service and timer for automatic replication
- cat > /mnt/etc/systemd/system/zfs-replicate.service << 'EOF'
-[Unit]
-Description=ZFS Replication to TrueNAS
-After=network-online.target
-Wants=network-online.target
-
-[Service]
-Type=oneshot
-ExecStart=/usr/local/bin/zfs-replicate
-User=root
-
-[Install]
-WantedBy=multi-user.target
-EOF
+read -p "Type 'ROLLBACK' to confirm: " confirm
+if [[ "$confirm" != "ROLLBACK" ]]; then
+ echo "Aborted."
+ exit 1
+fi
- cat > /mnt/etc/systemd/system/zfs-replicate.timer << 'EOF'
-[Unit]
-Description=Run ZFS replication nightly
+echo ""
+echo "Rolling back to genesis..."
-[Timer]
-OnCalendar=*-*-* 02:00:00
-RandomizedDelaySec=1800
-Persistent=true
+# Roll back each dataset (must do in reverse order for dependencies)
+zfs list -r -H -o name "${POOL_NAME}" | tac | while read dataset; do
+ if zfs list -t snapshot "${dataset}@genesis" &>/dev/null; then
+ echo " Rolling back: $dataset"
+ zfs rollback -r "${dataset}@genesis"
+ fi
+done
-[Install]
-WantedBy=timers.target
-EOF
+echo ""
+echo "Rollback complete!"
+echo "Reboot to complete the process: reboot"
+ROLLBACK_EOF
- info "Syncoid replication script created."
- info "Edit /usr/local/bin/zfs-replicate to configure your TrueNAS connection."
- info "Enable with: systemctl enable --now zfs-replicate.timer"
+ chmod +x /mnt/root/rollback-to-genesis
+ info "Genesis snapshot created. Rollback script: /root/rollback-to-genesis"
}
-### Unmount and Export ###
cleanup() {
step "Cleaning Up"
+ # Clear sensitive variables
+ ROOT_PASSWORD=""
+ ZFS_PASSPHRASE=""
+
info "Unmounting filesystems..."
umount /mnt/boot 2>/dev/null || true
@@ -747,85 +932,73 @@ cleanup() {
info "Cleanup complete."
}
-### Print Summary ###
print_summary() {
echo ""
- echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}"
- echo -e "${GREEN}║ Installation Complete! ║${NC}"
- echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════╝${NC}"
+ echo "╔═══════════════════════════════════════════════════════════════╗"
+ echo "║ Installation Complete! ║"
+ echo "╚═══════════════════════════════════════════════════════════════╝"
echo ""
- echo -e "${BOLD}System Configuration:${NC}"
+ echo "System Configuration:"
echo " Hostname: $HOSTNAME"
- echo " Username: $USERNAME"
echo " Timezone: $TIMEZONE"
echo " ZFS Pool: $POOL_NAME (encrypted)"
echo ""
- echo -e "${BOLD}ZFS Features Configured:${NC}"
- echo " - Automatic snapshots via sanoid (hourly/daily/weekly/monthly)"
+ echo "ZFS Features:"
+ echo " - Genesis snapshot: pristine post-install state"
echo " - Pre-pacman snapshots for safe upgrades"
- echo " - Replication script ready for TrueNAS"
- echo ""
- echo -e "${BOLD}Next Steps:${NC}"
- echo " 1. Reboot: ${CYAN}reboot${NC}"
- echo " 2. Enter your ZFS encryption passphrase at boot"
- echo " 3. Log in as $USERNAME"
- echo " 4. Run archsetup: ${CYAN}cd ~/code/archsetup && sudo ./archsetup${NC}"
+ echo " - Sanoid/syncoid configured by archsetup"
echo ""
- echo -e "${BOLD}Configure TrueNAS Replication:${NC}"
- echo " 1. Set up SSH key auth to TrueNAS"
- echo " 2. Edit: ${CYAN}/usr/local/bin/zfs-replicate${NC}"
- echo " 3. Enable: ${CYAN}sudo systemctl enable --now zfs-replicate.timer${NC}"
+ echo "Next Steps:"
+ echo " 1. Reboot: reboot"
+ echo " 2. Enter ZFS encryption passphrase at boot"
+ echo " 3. Log in as root"
+ echo " 4. Run archsetup: archsetup"
echo ""
- echo -e "${BOLD}Useful ZFS Commands:${NC}"
- echo " List snapshots: ${CYAN}zfs list -t snapshot${NC}"
- echo " Manual snapshot: ${CYAN}sudo zfs snapshot zroot/home@my-snapshot${NC}"
- echo " Rollback: ${CYAN}sudo zfs rollback zroot/home@my-snapshot${NC}"
- echo " Check pool status: ${CYAN}zpool status${NC}"
+ echo "Useful Commands:"
+ echo " List snapshots: zfs list -t snapshot"
+ echo " Manual snapshot: zfs snapshot zroot/home@my-backup"
+ echo " Rollback: zfs rollback zroot/home@my-backup"
+ echo " Factory reset: /root/rollback-to-genesis"
+ echo " Pool status: zpool status"
echo ""
- echo -e "${BOLD}If Something Goes Wrong:${NC}"
- echo " Boot from this ISO, then:"
- echo " ${CYAN}zpool import -R /mnt zroot${NC}"
- echo " ${CYAN}zfs load-key zroot${NC}"
- echo " ${CYAN}zfs mount zroot/ROOT/default${NC}"
- echo ""
- info "Installation log saved to: $LOGFILE"
+ info "Installation log: $LOGFILE"
echo ""
}
-### Main Installation Flow ###
+#############################
+# Main
+#############################
+
main() {
- echo ""
- echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
- echo -e "${CYAN}║ Arch Linux ZFS Root Installation ║${NC}"
- echo -e "${CYAN}║ with Native Encryption ║${NC}"
- echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
- echo ""
+ preflight_checks
+ gather_input
- info "Installation log: $LOGFILE"
+ # Unattended installation begins
+ echo ""
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "Beginning unattended installation..."
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
- configure_install
- select_disk
- partition_disk
+ partition_disks
create_zfs_pool
create_datasets
mount_efi
install_base
configure_system
+ configure_wifi
+ configure_ssh
configure_initramfs
configure_bootloader
configure_zfs_services
- configure_sanoid
configure_pacman_hook
- create_user
copy_archsetup
- create_syncoid_script
+ sync_efi_partitions
+ create_genesis_snapshot
cleanup
print_summary
}
-# Handle interrupts
trap 'error "Installation interrupted!"' INT TERM
-# Run main
main "$@"