-
-
Save mx00s/ea2462a3fe6fdaa65692fe7ee824de3e to your computer and use it in GitHub Desktop.
#!/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 |
I only saw that message appear in dmesg
's output, not journalctl -b
-- maybe try looking there? I was using linuxPackages_latest
when I posted my earlier comment (which was 5.6.xx
at the time).
When I run cat /sys/block/sda/queue/scheduler
, I can only see [mq-deadline] kyber none
as options -- noop
doesn't seem to exist (maybe it's an internal internal setting?). Though I wonder if the docs are outdated, considering mq-deadline
and kyber
are missing.
I finally figured out how to set the scheduler to none
for ZFS partitions using services.udev.extraRules
:
Thanks to this Reddit post: https://www.reddit.com/r/zfs/comments/iluh00/zfs_linux_io_scheduler/g3uxm3m/
@mx00s thanks for this script! With NixOS 21.11 you'll need to add isNormalUser = true;
to L236.
Most guides seem to also call for -o ashift=12
and -o autotrim=on
but I'm no zfs expert.
@mx00s @operator-name @cole-h @grahamc
Does this work because I'm about to try it and don't want any unwelcome surprises.
I started looking into this on my NixOS 20.03 (kernel `5.4.43) and haven't seen any errors like what @cole-h reports.
Mention of the parameter was removed from docs ini rev
f97eeb6cf
of the kernel and the error message was introduced inf8db3835
. I haven't worked out yet how to specify the desired behavior using sysfs/NixOS.Notably, the only options supported for
elevator=
according toDocumentation/block/switching-sched.rst
in the first rev weredeadline
,noop
, andcfq
(default). @grahamc, did you perhaps mean to usenoop
instead ofnone
?