Skip to content

Instantly share code, notes, and snippets.

@byrongibson
Forked from mx00s/install.sh
Created May 23, 2021 05:06
Show Gist options
  • Select an option

  • Save byrongibson/fe8e24d2433d2e082bc238315c0b3483 to your computer and use it in GitHub Desktop.

Select an option

Save byrongibson/fe8e24d2433d2e082bc238315c0b3483 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/)
#
# 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 "$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 "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')"
export NIXOS_CONFIG_PATH=/mnt/etc/nixos/configuration.nix
export NIXOS_CONFIG_BACKUP_PATH="${NIXOS_CONFIG_PATH}.original"
if [[ -f "$NIXOS_CONFIG_BACKUP_PATH" ]]; then
info "Restoring copy of the original NixOS config to '${NIXOS_CONFIG_PATH}' ..."
cp "$NIXOS_CONFIG_BACKUP_PATH" "$NIXOS_CONFIG_PATH"
else
info "Saving a copy of the original '${NIXOS_CONFIG_PATH}' to '${NIXOS_CONFIG_BACKUP_PATH}' ..."
cp "$NIXOS_CONFIG_PATH" "$NIXOS_CONFIG_BACKUP_PATH"
fi
info "Injecting extra configuration into /mnt/etc/nixos/configuration.nix ..."
EXTRA_NIXOS_CONFIG="
# 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)\";
users = {
mutableUsers = false;
users.root.initialHashedPassword = \"${ROOT_PASSWORD_HASH}\";
users.root.openssh.authorizedKeys.keys = [
\"${AUTHORIZED_SSH_KEY}\"
];
};
services.openssh = {
enable = true;
hostKeys = [
{
path = \"/persist/etc/ssh/ssh_host_ed25519_key\";
type = \"ed25519\";
}
{
path = \"/persist/etc/ssh/ssh_host_rsa_key\";
type = \"rsa\";
bits = 4096;
}
];
};
"
nix-shell -p ed --command "ed \"${NIXOS_CONFIG_PATH}\" <<EOF
16i${EXTRA_NIXOS_CONFIG}.
wq
EOF
"
sed -i "s/{ config, pkgs, ... }:/{ config, pkgs, lib, ... }:/" "$NIXOS_CONFIG_PATH"
info "Installing NixOS to /mnt ..."
nixos-install --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