Skip to content

Instantly share code, notes, and snippets.

@jbott
Forked from mx00s/install.sh
Last active August 22, 2023 19:09
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jbott/531b9d555dae7f197f25326ef251f164 to your computer and use it in GitHub Desktop.
Save jbott/531b9d555dae7f197f25326ef251f164 to your computer and use it in GitHub Desktop.
NixOS install script based on @grahamc's "Erase Your Darlings" blog post
#!/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