-
-
Save jbott/531b9d555dae7f197f25326ef251f164 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/) | |
# - Original gist by @nuxeh (https://gist.github.com/nuxeh/35fb0eca2af4b305052ca11fe2521159) | |
# | |
# It expects the name of the block device (e.g. 'sda') to partition and install | |
# NixOS on, a non-root username, and an authorized public ssh key to log in as | |
# that user remotely. The script must also be executed as root. | |
# | |
# Example: `sudo ./install.sh /dev/sde only-slightly-bent jbo "ssh-rsa AAAAB..."` | |
# | |
# Changes from the original gist: | |
# - updated user creation | |
# - do not set passwords for any user. There's some danger in this! | |
# - create a larger boot partition (2GiB rather than 512MiB) | |
# | |
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}" | |
} | |
################################################################################ | |
# | |
if [[ $# != 2 && $# != 4 ]]; then | |
echo "usage: sudo bash install.sh /dev/<disk> hostname [username \"ssh-rsa ...\"]" | |
exit 1 | |
fi | |
export DISK=$1 | |
export HOST_NAME=$2 | |
export USER_NAME=${3:-jbo} | |
export AUTHORIZED_SSH_KEY=${4:-"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCe728NHek6pPPMX1Pn6pCV49U0Fl399m+nKnG9MPVl8CQ84Qwo0JQlnernqmi245LT307jcW+beT2eAkTmGqH4= jbo@Just-Another-Victim-of-the-Ambient-Morality"} | |
if ! [[ "${DISK}" =~ ^/dev/ ]]; then | |
export DISK_PATH="/dev/${DISK}" | |
else | |
export DISK_PATH="${DISK}" | |
fi | |
if ! [[ -b "$DISK_PATH" ]]; then | |
err "Invalid argument: '${DISK_PATH}' is not a block special file" | |
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 --script "$DISK_PATH" -- mklabel gpt | |
parted --script "$DISK_PATH" -- mkpart primary 2GiB 100% | |
parted --script "$DISK_PATH" -- mkpart ESP fat32 1MiB 2GiB | |
parted --script "$DISK_PATH" -- set 2 boot on | |
if [[ "${DISK_PATH}" =~ nvme ]]; then | |
export DISK_PART_ROOT="${DISK_PATH}p1" | |
export DISK_PART_BOOT="${DISK_PATH}p2" | |
else | |
export DISK_PART_ROOT="${DISK_PATH}1" | |
export DISK_PART_BOOT="${DISK_PATH}2" | |
fi | |
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 "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" | |
]; | |
settings = { | |
experimental-features = ["nix-command flakes"]; | |
trusted-users = ["${USER_NAME}"]; | |
}; | |
}; | |
# 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://github.com/cole-h/nixos-config/blob/3589d53515921772867065150c6b5a500a5b9a6b/hosts/scadrial/modules/services.nix#L70 | |
# services.udev.packages for packages with udev rules | |
# SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eed2", TAG+="uaccess", RUN{builtin}+="uaccess" | |
# Set noop scheduler for zfs partitions | |
services.udev.extraRules = '' | |
KERNEL=="sd[a-z]*[0-9]*|mmcblk[0-9]*p[0-9]*|nvme[0-9]*n[0-9]*p[0-9]*", ENV{ID_FS_TYPE}=="zfs_member", ATTR{../queue/scheduler}="none" | |
''; | |
networking.hostName = "${HOST_NAME}"; | |
networking.hostId = "$(head -c 8 /etc/machine-id)"; | |
environment.systemPackages = with pkgs; | |
[ | |
vim | |
]; | |
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 = { | |
${USER_NAME} = { | |
isNormalUser = true; | |
createHome = true; | |
extraGroups = [ "wheel" ]; | |
group = "${USER_NAME}"; | |
uid = 1000; | |
home = "/home/${USER_NAME}"; | |
useDefaultShell = true; | |
openssh.authorizedKeys.keys = [ "${AUTHORIZED_SSH_KEY}" ]; | |
}; | |
}; | |
groups = { | |
${USER_NAME} = { | |
gid = 1000; | |
}; | |
}; | |
}; | |
# Since we don't set any user password, allow passwordless sudo | |
security.sudo.wheelNeedsPassword = false; | |
# 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 = "22.11"; # 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