Last active
February 26, 2026 03:26
-
-
Save Reclyptor/bb8c843e3ffb066bb734c0b2fc4bd84f to your computer and use it in GitHub Desktop.
NixOS Node Installation Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| HOSTNAME="${1:-}" | |
| DISK="${2:-}" | |
| REPO_URL="https://github.com/Reclyptor/NixOS.git" | |
| FLAKE_DIR="/tmp/nixos-config" | |
| VALID_HOSTS="archeon fluxeon voideon styxeon bytheon" | |
| # Where the installed system will read the Age identity file from. | |
| # This MUST match your sops-nix config (sops.age.keyFile). | |
| # If yours differs, change it here. | |
| SOPS_KEY_TARGET="/mnt/etc/sops/age/keys.txt" | |
| usage() { | |
| echo "Usage: $0 <hostname> <disk>" | |
| echo "" | |
| echo " hostname One of: $VALID_HOSTS" | |
| echo " disk Target disk (e.g. /dev/nvme0n1 or nvme0n1, /dev/sda or sda)" | |
| echo "" | |
| echo "Example: $0 fluxeon /dev/nvme0n1" | |
| exit 1 | |
| } | |
| [ -z "$HOSTNAME" ] || [ -z "$DISK" ] && usage | |
| echo "$VALID_HOSTS" | grep -qw "$HOSTNAME" || { echo "Error: unknown host '$HOSTNAME'"; usage; } | |
| # Allow passing "nvme0n1" instead of "/dev/nvme0n1" | |
| if [[ "$DISK" != /dev/* ]]; then | |
| DISK="/dev/$DISK" | |
| fi | |
| [ -b "$DISK" ] || { echo "Error: '$DISK' is not a block device"; exit 1; } | |
| if [[ "$DISK" == *"nvme"* ]] || [[ "$DISK" == *"loop"* ]]; then | |
| PART="${DISK}p" | |
| else | |
| PART="${DISK}" | |
| fi | |
| echo "=== NixOS + ZFS Install: $HOSTNAME on $DISK ===" | |
| echo "" | |
| read -p "This will DESTROY all data on $DISK. Continue? [y/N] " -r | |
| [[ "${REPLY:-}" =~ ^[Yy]$ ]] || exit 0 | |
| # ── 1. Partition ────────────────────────────────────────────────────── | |
| echo "[1/10] Partitioning $DISK..." | |
| sgdisk --zap-all "$DISK" | |
| sgdisk -n 1:0:+1G -t 1:EF00 -c 1:"boot" "$DISK" | |
| sgdisk -n 2:0:0 -t 2:BF00 -c 2:"zfs" "$DISK" | |
| partprobe "$DISK" | |
| sleep 2 | |
| # ── 2. Format EFI ──────────────────────────────────────────────────── | |
| echo "[2/10] Formatting EFI partition..." | |
| mkfs.fat -F 32 "${PART}1" | |
| # ── 3. Create ZFS pool ─────────────────────────────────────────────── | |
| echo "[3/10] Creating ZFS pool (rpool)..." | |
| zpool create -f \ | |
| -o ashift=12 \ | |
| -O acltype=posixacl \ | |
| -O xattr=sa \ | |
| -O dnodesize=auto \ | |
| -O normalization=formD \ | |
| -O mountpoint=none \ | |
| -O canmount=off \ | |
| -O compression=zstd \ | |
| -R /mnt \ | |
| rpool "${PART}2" | |
| # ── 4. Create datasets ─────────────────────────────────────────────── | |
| echo "[4/10] Creating ZFS datasets..." | |
| zfs create -o canmount=off -o mountpoint=none rpool/nixos | |
| zfs create -o mountpoint=legacy rpool/nixos/root | |
| zfs create -o mountpoint=legacy rpool/nixos/home | |
| zfs create -o mountpoint=legacy rpool/nixos/nix | |
| # ── 5. Mount ───────────────────────────────────────────────────────── | |
| echo "[5/10] Mounting filesystems..." | |
| mount -t zfs rpool/nixos/root /mnt | |
| mkdir -p /mnt/{home,nix,boot} | |
| mount -t zfs rpool/nixos/home /mnt/home | |
| mount -t zfs rpool/nixos/nix /mnt/nix | |
| mount "${PART}1" /mnt/boot | |
| # ── 6. Clone flake and patch boot UUID ──────────────────────────────── | |
| echo "[6/10] Cloning NixOS configuration..." | |
| BOOT_UUID="$(blkid -s UUID -o value "${PART}1")" | |
| nix-shell -p git --run "git clone $REPO_URL $FLAKE_DIR" | |
| sed -i "s|XXXX-XXXX|$BOOT_UUID|" "$FLAKE_DIR/hosts/$HOSTNAME/hardware-configuration.nix" | |
| echo " Boot UUID: $BOOT_UUID" | |
| # ── 7. Generate SSH host keys ───────────────────────────────────────── | |
| echo "[7/10] Generating SSH host keys..." | |
| mkdir -p /mnt/etc/ssh | |
| ssh-keygen -t ed25519 -f /mnt/etc/ssh/ssh_host_ed25519_key -N "" -q | |
| ssh-keygen -t rsa -b 4096 -f /mnt/etc/ssh/ssh_host_rsa_key -N "" -q | |
| AGE_KEY="$(nix-shell -p ssh-to-age --run "ssh-to-age < /mnt/etc/ssh/ssh_host_ed25519_key.pub")" | |
| echo " Age public key: $AGE_KEY" | |
| # ── 8. YubiKey decrypt bootstrap (NO updatekeys) ────────────────────── | |
| echo "[8/10] Preparing YubiKey decrypt material for install (no updatekeys)..." | |
| YUBIKEY_IDENTITY="/tmp/yubikey-identity.txt" | |
| echo " Ensuring pcscd is running and CCID driver is available..." | |
| sudo pkill -9 pcscd 2>/dev/null || true | |
| sudo rm -f /run/pcscd/pcscd.comm | |
| sudo rm -rf /var/lib/pcsc/drivers | |
| sudo mkdir -p /var/lib/pcsc/drivers | |
| # Deterministic ccid path (no flakes / no nix-command required) | |
| CCID_OUT="$(nix-instantiate --eval -E '(import <nixpkgs> {}).ccid.outPath' | tr -d '"')" | |
| CCID_BUNDLE="$CCID_OUT/pcsc/drivers/ifd-ccid.bundle" | |
| if [[ ! -e "$CCID_BUNDLE" ]]; then | |
| echo "ERROR: CCID bundle not found at: $CCID_BUNDLE" | |
| exit 1 | |
| fi | |
| sudo ln -sf "$CCID_BUNDLE" /var/lib/pcsc/drivers/ | |
| # Start pcscd in foreground (keep it alive for the duration of nixos-install) | |
| sudo nix-shell -p pcsclite ccid --run "pcscd --foreground" & | |
| sleep 2 | |
| echo " Generating yubikey identity..." | |
| # IMPORTANT: redirect inside the privileged shell so the file is written correctly | |
| sudo nix-shell -p age-plugin-yubikey --run "age-plugin-yubikey --identity --slot 1 > $YUBIKEY_IDENTITY" | |
| echo " Installing identity into target system at: $SOPS_KEY_TARGET" | |
| sudo mkdir -p "$(dirname "$SOPS_KEY_TARGET")" | |
| sudo cp -f "$YUBIKEY_IDENTITY" "$SOPS_KEY_TARGET" | |
| sudo chmod 600 "$SOPS_KEY_TARGET" | |
| # ── 8b. Persist NetworkManager Wi-Fi profiles into target system ────── | |
| echo "[8b/10] Persisting NetworkManager Wi-Fi profiles (if present)..." | |
| sudo mkdir -p /mnt/etc/NetworkManager/system-connections | |
| sudo chmod 700 /mnt/etc/NetworkManager/system-connections | |
| # Use sudo bash so '*' expands as root (works even if you can't list as user) | |
| if sudo bash -lc 'ls -1 /etc/NetworkManager/system-connections/* >/dev/null 2>&1'; then | |
| sudo bash -lc 'cp -a /etc/NetworkManager/system-connections/* /mnt/etc/NetworkManager/system-connections/' | |
| echo " Copied profiles from /etc/NetworkManager/system-connections" | |
| elif sudo bash -lc 'ls -1 /run/NetworkManager/system-connections/* >/dev/null 2>&1'; then | |
| sudo bash -lc 'cp -a /run/NetworkManager/system-connections/* /mnt/etc/NetworkManager/system-connections/' | |
| echo " Copied profiles from /run/NetworkManager/system-connections" | |
| else | |
| echo " No NetworkManager profiles found to copy." | |
| echo " Tip: connect on the live ISO using nmcli first, then rerun." | |
| fi | |
| sudo bash -lc 'chmod 600 /mnt/etc/NetworkManager/system-connections/* 2>/dev/null || true' | |
| # ── 9. Install ──────────────────────────────────────────────────────── | |
| echo "[9/10] Installing NixOS (this will take a while)..." | |
| nixos-install --root /mnt --flake "$FLAKE_DIR#$HOSTNAME" | |
| # ── 10. Set user password ───────────────────────────────────────────── | |
| echo "[10/10] Setting password for reclyptor..." | |
| sudo nixos-enter --root /mnt -- passwd reclyptor | |
| echo "" | |
| echo "=== Installation complete ===" | |
| echo "" | |
| echo "To unmount and reboot:" | |
| echo " umount /mnt/boot /mnt/home /mnt/nix" | |
| echo " umount -l /mnt" | |
| echo " zpool export rpool" | |
| echo " reboot" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment