Skip to content

Instantly share code, notes, and snippets.

@lucasvo
Created October 17, 2021 19:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucasvo/35e0745b72dd384dcb9b9ee5bae5fecb to your computer and use it in GitHub Desktop.
Save lucasvo/35e0745b72dd384dcb9b9ee5bae5fecb to your computer and use it in GitHub Desktop.
NixOS with ZFS
#!/usr/bin/env bash
#
# NixOS install script synthesized from:
#
# - https://gist.github.com/mx00s/ea2462a3fe6fdaa65692fe7ee824de3e
# - Erase Your Darlings (https://grahamc.com/blog/erase-your-darlings)
# - OpenZFS NixOS manual (https://openzfs.github.io/openzfs-docs/Getting%20Started/NixOS/Root%20on%20ZFS/0-overview.html)
# - 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. The script must also be executed as root.
#
# Example: `sudo ./install.sh sde`
#
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
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 [[ "$EUID" > 0 ]]; then
err "Must run as root"
exit 1
fi
export ZFS_POOL="rpool"
# ephemeral datasets
export ZFS_ENCRYPTED="${ZFS_POOL}/encrypted"
export ZFS_LOCAL="${ZFS_ENCRYPTED}/local"
export ZFS_DS_ROOT="${ZFS_LOCAL}/root"
export ZFS_DS_NIX="${ZFS_LOCAL}/nix"
# persistent datasets
export ZFS_SAFE="${ZFS_ENCRYPTED}/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 ..."
blkdiscard -f "$DISK_PATH"
sgdisk --zap-all "$DISK_PATH"
sgdisk -n 0:1M:+513M -t 0:EF00 "$DISK_PATH"
sgdisk -n 0:0:+24G -t 0:8200 "$DISK_PATH"
sgdisk -n 0:0:0 -t 0:EF00 "$DISK_PATH"
export BOOT="${DISK_PATH}p1"
export SWAP="${DISK_PATH}p2"
export ZFS="${DISK_PATH}p3"
partprobe "$DISK_PATH"
sleep 1
BOOT_DISK_UUID="$(blkid --match-tag UUID --output value $BOOT)"
SWAP_DISK_PARTUUID="$(blkid --match-tag PARTUUID --output value $SWAP)"
info "Formatting boot & swap partition ..."
mkfs.vfat -n boot "$BOOT"
mkswap $SWAP
swapon $SWAP
info "Creating '$ZFS_POOL' ZFS pool for '$ZFS' ..."
zpool create \
-O acltype=posixacl \
-O compression=on \
-O normalization=formD \
-O xattr=sa \
-f $ZFS_POOL $ZFS
ZFS_DISK_UUID="$(blkid --match-tag UUID --output value $ZFS)"
info "Creating '$ZFS_ENCRYPTED' ZFS pool for '$ZFS' ..."
zfs create \
-p \
-o mountpoint=legacy \
-o encryption=aes-256-gcm \
-o keylocation=prompt \
-o keyformat=passphrase \
"$ZFS_DS_ROOT"
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 '$BOOT' to /mnt/boot ..."
mkdir -p /mnt/boot
mount -t vfat "$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 -p /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 -p /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 -p /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 directories ..."
mkdir -p /mnt/persist/etc/ssh
mkdir -p /persist/etc/NetworkManager/system-connections
mkdir -p /persist/var/lib/bluetooth
mkdir -p /persist/etc/ssh
info "Generating NixOS configuration (/mnt/etc/nixos/*.nix) ..."
nixos-generate-config --root /mnt
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
NIXEL="$(dirname "$(readlink -fm "$0")")"
cp -r $NIXEL /mnt/persist/etc/nixel
info "Writing NixOS configuration to /persist/etc/nixos/ ..."
cat <<EOF > /mnt/persist/etc/nixos/configuration.nix
{ config, pkgs, lib, ... }:
{
imports =
[
../nixel/configuration.nix
./hardware-configuration.nix
];
fileSystems."/" =
{ device = "rpool/encrypted/local/root";
fsType = "zfs";
options= [ "zfsutil" ];
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/${BOOT_DISK_UUID}";
fsType = "vfat";
};
fileSystems."/nix" =
{ device = "rpool/encrypted/local/nix";
fsType = "zfs";
options= ["zfsutil"];
};
fileSystems."/home" =
{ device = "rpool/encrypted/safe/home";
fsType = "zfs";
options= ["zfsutil"];
};
fileSystems."/persist" =
{ device = "rpool/encrypted/safe/persist";
fsType = "zfs";
options= ["zfsutil"];
};
swapDevices = [
{ device = "/dev/disk/by-partuuid/${SWAP_DISK_PARTUUID}"; randomEncryption.enable = true;}
];
boot.supportedFilesystems = [ "zfs" ];
boot.zfs.devNodes="${DISK_PATH}";
boot.initrd.postDeviceCommands = lib.mkAfter ''
zfs rollback -r ${ZFS_BLANK_SNAPSHOT}
'';
networking = {
hostId = "$(head -c 8 /etc/machine-id)";
};
environment.etc."machine-id".text = "$(cat /etc/machine-id)";
services.zfs = {
autoScrub.enable = true;
autoSnapshot.enable = true;
# TODO: autoReplication
};
users = {
mutableUsers = false;
users = {
${USER_NAME} = {
initialHashedPassword = "${USER_PASSWORD_HASH}";
extraGroups = [ "wheel" "nixel" "audio" "video" "bluetooth" "networkmanager" "docker" "libvirtd"];
isNormalUser = true;
uid = 1000;
home = "/home/${USER_NAME}";
};
};
};
}
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
@jammus
Copy link

jammus commented Jan 27, 2024

Thanks for this, it's been super helpful

I think line 113 should be "$ZFS_ENCRYPTED"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment