Last active
May 30, 2024 18:45
-
-
Save mx00s/ea2462a3fe6fdaa65692fe7ee824de3e to your computer and use it in GitHub Desktop.
NixOS install script based on @grahamc's "Erase Your Darlings" blog post
This file contains 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 | |
# | |
# NixOS install script synthesized from: | |
# | |
# - Erase Your Darlings (https://grahamc.com/blog/erase-your-darlings) | |
# - ZFS Datasets for NixOS (https://grahamc.com/blog/nixos-on-zfs) | |
# - NixOS Manual (https://nixos.org/nixos/manual/) | |
# | |
# It expects the name of the block device (e.g. 'sda') to partition | |
# and install NixOS on and an authorized public ssh key to log in as | |
# 'root' remotely. The script must also be executed as root. | |
# | |
# Example: `sudo ./install.sh sde "ssh-rsa AAAAB..."` | |
# | |
set -euo pipefail | |
################################################################################ | |
export COLOR_RESET="\033[0m" | |
export RED_BG="\033[41m" | |
export BLUE_BG="\033[44m" | |
function err { | |
echo -e "${RED_BG}$1${COLOR_RESET}" | |
} | |
function info { | |
echo -e "${BLUE_BG}$1${COLOR_RESET}" | |
} | |
################################################################################ | |
export DISK=$1 | |
export AUTHORIZED_SSH_KEY=$2 | |
if ! [[ -v DISK ]]; then | |
err "Missing argument. Expected block device name, e.g. 'sda'" | |
exit 1 | |
fi | |
export DISK_PATH="/dev/${DISK}" | |
if ! [[ -b "$DISK_PATH" ]]; then | |
err "Invalid argument: '${DISK_PATH}' is not a block special file" | |
exit 1 | |
fi | |
if ! [[ -v AUTHORIZED_SSH_KEY ]]; then | |
err "Missing argument. Expected public SSH key, e.g. 'ssh-rsa AAAAB...'" | |
exit 1 | |
fi | |
if [[ "$EUID" > 0 ]]; then | |
err "Must run as root" | |
exit 1 | |
fi | |
export ZFS_POOL="rpool" | |
# ephemeral datasets | |
export ZFS_LOCAL="${ZFS_POOL}/local" | |
export ZFS_DS_ROOT="${ZFS_LOCAL}/root" | |
export ZFS_DS_NIX="${ZFS_LOCAL}/nix" | |
# persistent datasets | |
export ZFS_SAFE="${ZFS_POOL}/safe" | |
export ZFS_DS_HOME="${ZFS_SAFE}/home" | |
export ZFS_DS_PERSIST="${ZFS_SAFE}/persist" | |
export ZFS_BLANK_SNAPSHOT="${ZFS_DS_ROOT}@blank" | |
################################################################################ | |
info "Running the UEFI (GPT) partitioning and formatting directions from the NixOS manual ..." | |
parted "$DISK_PATH" -- mklabel gpt | |
parted "$DISK_PATH" -- mkpart primary 512MiB 100% | |
parted "$DISK_PATH" -- mkpart ESP fat32 1MiB 512MiB | |
parted "$DISK_PATH" -- set 2 boot on | |
export DISK_PART_ROOT="${DISK_PATH}1" | |
export DISK_PART_BOOT="${DISK_PATH}2" | |
info "Formatting boot partition ..." | |
mkfs.fat -F 32 -n boot "$DISK_PART_BOOT" | |
info "Creating '$ZFS_POOL' ZFS pool for '$DISK_PART_ROOT' ..." | |
zpool create -f "$ZFS_POOL" "$DISK_PART_ROOT" | |
info "Enabling compression for '$ZFS_POOL' ZFS pool ..." | |
zfs set compression=on "$ZFS_POOL" | |
info "Creating '$ZFS_DS_ROOT' ZFS dataset ..." | |
zfs create -p -o mountpoint=legacy "$ZFS_DS_ROOT" | |
info "Configuring extended attributes setting for '$ZFS_DS_ROOT' ZFS dataset ..." | |
zfs set xattr=sa "$ZFS_DS_ROOT" | |
info "Configuring access control list setting for '$ZFS_DS_ROOT' ZFS dataset ..." | |
zfs set acltype=posixacl "$ZFS_DS_ROOT" | |
info "Creating '$ZFS_BLANK_SNAPSHOT' ZFS snapshot ..." | |
zfs snapshot "$ZFS_BLANK_SNAPSHOT" | |
info "Mounting '$ZFS_DS_ROOT' to /mnt ..." | |
mount -t zfs "$ZFS_DS_ROOT" /mnt | |
info "Mounting '$DISK_PART_BOOT' to /mnt/boot ..." | |
mkdir /mnt/boot | |
mount -t vfat "$DISK_PART_BOOT" /mnt/boot | |
info "Creating '$ZFS_DS_NIX' ZFS dataset ..." | |
zfs create -p -o mountpoint=legacy "$ZFS_DS_NIX" | |
info "Disabling access time setting for '$ZFS_DS_NIX' ZFS dataset ..." | |
zfs set atime=off "$ZFS_DS_NIX" | |
info "Mounting '$ZFS_DS_NIX' to /mnt/nix ..." | |
mkdir /mnt/nix | |
mount -t zfs "$ZFS_DS_NIX" /mnt/nix | |
info "Creating '$ZFS_DS_HOME' ZFS dataset ..." | |
zfs create -p -o mountpoint=legacy "$ZFS_DS_HOME" | |
info "Mounting '$ZFS_DS_HOME' to /mnt/home ..." | |
mkdir /mnt/home | |
mount -t zfs "$ZFS_DS_HOME" /mnt/home | |
info "Creating '$ZFS_DS_PERSIST' ZFS dataset ..." | |
zfs create -p -o mountpoint=legacy "$ZFS_DS_PERSIST" | |
info "Mounting '$ZFS_DS_PERSIST' to /mnt/persist ..." | |
mkdir /mnt/persist | |
mount -t zfs "$ZFS_DS_PERSIST" /mnt/persist | |
info "Permit ZFS auto-snapshots on ${ZFS_SAFE}/* datasets ..." | |
zfs set com.sun:auto-snapshot=true "$ZFS_DS_HOME" | |
zfs set com.sun:auto-snapshot=true "$ZFS_DS_PERSIST" | |
info "Creating persistent directory for host SSH keys ..." | |
mkdir -p /mnt/persist/etc/ssh | |
info "Generating NixOS configuration (/mnt/etc/nixos/*.nix) ..." | |
nixos-generate-config --root /mnt | |
info "Enter password for the root user ..." | |
ROOT_PASSWORD_HASH="$(mkpasswd -m sha-512 | sed 's/\$/\\$/g')" | |
info "Enter personal user name ..." | |
read USER_NAME | |
info "Enter password for '${USER_NAME}' user ..." | |
USER_PASSWORD_HASH="$(mkpasswd -m sha-512 | sed 's/\$/\\$/g')" | |
info "Moving generated hardware-configuration.nix to /persist/etc/nixos/ ..." | |
mkdir -p /mnt/persist/etc/nixos | |
mv /mnt/etc/nixos/hardware-configuration.nix /mnt/persist/etc/nixos/ | |
info "Backing up the originally generated configuration.nix to /persist/etc/nixos/configuration.nix.original ..." | |
mv /mnt/etc/nixos/configuration.nix /mnt/persist/etc/nixos/configuration.nix.original | |
info "Backing up the this installer script to /persist/etc/nixos/install.sh.original ..." | |
cp "$0" /mnt/persist/etc/nixos/install.sh.original | |
info "Writing NixOS configuration to /persist/etc/nixos/ ..." | |
cat <<EOF > /mnt/persist/etc/nixos/configuration.nix | |
{ config, pkgs, lib, ... }: | |
{ | |
imports = | |
[ | |
./hardware-configuration.nix | |
]; | |
nix.nixPath = | |
[ | |
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos" | |
"nixos-config=/persist/etc/nixos/configuration.nix" | |
"/nix/var/nix/profiles/per-user/root/channels" | |
]; | |
# Use the systemd-boot EFI boot loader. | |
boot.loader.systemd-boot.enable = true; | |
boot.loader.efi.canTouchEfiVariables = true; | |
# source: https://grahamc.com/blog/erase-your-darlings | |
boot.initrd.postDeviceCommands = lib.mkAfter '' | |
zfs rollback -r ${ZFS_BLANK_SNAPSHOT} | |
''; | |
# source: https://grahamc.com/blog/nixos-on-zfs | |
boot.kernelParams = [ "elevator=none" ]; | |
networking.hostId = "$(head -c 8 /etc/machine-id)"; | |
networking.useDHCP = false; | |
networking.interfaces.enp3s0.useDHCP = true; | |
environment.systemPackages = with pkgs; | |
[ | |
emacs | |
]; | |
services.zfs = { | |
autoScrub.enable = true; | |
autoSnapshot.enable = true; | |
# TODO: autoReplication | |
}; | |
services.openssh = { | |
enable = true; | |
permitRootLogin = "no"; | |
passwordAuthentication = false; | |
hostKeys = | |
[ | |
{ | |
path = "/persist/etc/ssh/ssh_host_ed25519_key"; | |
type = "ed25519"; | |
} | |
{ | |
path = "/persist/etc/ssh/ssh_host_rsa_key"; | |
type = "rsa"; | |
bits = 4096; | |
} | |
]; | |
}; | |
users = { | |
mutableUsers = false; | |
users = { | |
root = { | |
initialHashedPassword = "${ROOT_PASSWORD_HASH}"; | |
}; | |
${USER_NAME} = { | |
createHome = true; | |
initialHashedPassword = "${USER_PASSWORD_HASH}"; | |
extraGroups = [ "wheel" ]; | |
group = "users"; | |
uid = 1000; | |
home = "/home/${USER_NAME}"; | |
useDefaultShell = true; | |
openssh.authorizedKeys.keys = [ "${AUTHORIZED_SSH_KEY}" ]; | |
}; | |
}; | |
}; | |
# This value determines the NixOS release from which the default | |
# settings for stateful data, like file locations and database versions | |
# on your system were taken. It‘s perfectly fine and recommended to leave | |
# this value at the release version of the first install of this system. | |
# Before changing this value read the documentation for this option | |
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). | |
system.stateVersion = "20.03"; # Did you read the comment? | |
} | |
EOF | |
info "Installing NixOS to /mnt ..." | |
ln -s /mnt/persist/etc/nixos/configuration.nix /mnt/etc/nixos/configuration.nix | |
nixos-install -I "nixos-config=/mnt/persist/etc/nixos/configuration.nix" --no-root-passwd # already prompted for and configured password |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@mx00s @operator-name @cole-h @grahamc
Does this work because I'm about to try it and don't want any unwelcome surprises.