This is a rather short post and the start of what will hopefully become a nice little series of posts related to NixOS.

I just had to bulk install NixOS on a bunch of Intel NUCs, here is a little script to automate the installation process. Some bits and pieces are taken from other installation scripts, but it’s been a while, so I don’t know who to credit for them, I’m fairly sure this wonderful post from the NixOS discourse was involved though. The whole script can be found here as gist as well.

Start with the usual #! and set the script up to exit on errors using set -e:

#!/usr/bin/env bash
set -e

Next add a function for pretty printing, (I stole this part from somewhere else).

pprint () {
    local cyan="\e[96m"
    local default="\e[39m"
    # ISO8601 timestamp + ms
    local timestamp
    timestamp=$(date +%FT%T.%3NZ)
    echo -e "${cyan}${timestamp} $1${default}" 1>&2

Instead of using a DISK variable and changing its value inside the script everytime it is run, simply let the user pick the device.

echo "These are the disks available:"
ls -l /dev/disk/by-id/ | awk '{print $11, $10, $9}' | tr -d './' | column -t

# Set DISK
select ENTRY in $(ls /dev/disk/by-id/);
    echo $ENTRY
    echo "Installing system on $ENTRY."

Next set up the SWAP Size:

echo -n "Enter Swap size (e.g. 10GiB, 5MiB)"
if [[ $SWAP_SIZE =~ ^[0-9]*(GiB|MiB)$ ]]
  echo "Swap size set to $SWAP_SIZE"
  echo "Please enter correct size"

Ask for confirmation and wipe the disk:

read -p "> Do you want to wipe all data on $ENTRY ?" -n 1 -r
echo # move to a new line
if [[ "$REPLY" =~ ^[Yy]$ ]]
    # Clear disk
    wipefs -af "$DISK"
    sgdisk -Zo "$DISK"
    partprobe $DISK
    exit 1

Initially I was using sgdisk in order to create all partitions. This usually seems to work, however I noticed, that on some hardware devices (NUC Canyons), the boot flag had to be set in order for them to actually boot up, so i switched to parted instead.

parted $DISK -- mklabel gpt

pprint "Creating system partition"
parted $DISK -- mkpart primary 512MiB -${SWAP_SIZE}

pprint "Creating swap partition"
parted $DISK -- mkpart primary linux-swap -${SWAP_SIZE} 100%

pprint "Creating boot (EFI) partition"
parted $DISK -- mkpart ESP fat32 1MiB 512MiB
parted $DISK -- set 3 boot on

pprint "Running partprobe on $DISK"
partprobe $DISK

Now that the partitions have been created, lets create filesystems and mount them. The sleep at the beginning seems to be necessary, otherwise there are problems with creating the swap partition.

sleep 2 # not sure why, but if we don't wait, the following swap does not work properly

pprint "Enable SWAP on $SWAP"
mkswap -L swap $SWAP
swapon -L swap

# ZFS partition
pprint "Create zfs pool rpool"
zpool create -f -O mountpoint=none -O acltype=posixacl -O xattr=sa -R /mnt rpool $ROOT

pprint "Create zfs dataset rpool/root"
zfs create -o mountpoint=none   rpool/root
pprint "Create zfs dataset rpool/root/nixos"
zfs create -o mountpoint=legacy rpool/root/nixos
pprint "Create zfs dataset rpool/home"
zfs create -o mountpoint=legacy rpool/home

pprint "Mount datasets: rpool/root/nixos"
mount -t zfs rpool/root/nixos /mnt

pprint "Mount datasets: rpool/home"
mkdir /mnt/home
mount -t zfs rpool/home /mnt/home

pprint "Mount boot partition"
mkfs.fat -F 32 -n boot $BOOT
mkdir /mnt/boot
mount $BOOT /mnt/boot

Finally create a nixos configuration and set the system up to boot from ZFS:

pprint "Generate NixOS configuration"
nixos-generate-config --root /mnt

# Add ZFS configuration
HOSTID=$(head -c8 /etc/machine-id)

networking.hostId = "$HOSTID";
boot.supportedFilesystems = [ "zfs" ];
boot.zfs.devNodes = "$ROOT";

#pprint "Append configuration to hardware-configuration.nix"
sed -i "\$ s/.}\$//;\$e cat $HARDWARE_CONFIG" /mnt/etc/nixos/configuration.nix

pprint "You can now install nixos using the 'nixos-install' command."