Skip to content

Instantly share code, notes, and snippets.

@osv
Last active April 28, 2023 13:56
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 osv/645fa0e747511c8a48a4f21ecfc411db to your computer and use it in GitHub Desktop.
Save osv/645fa0e747511c8a48a4f21ecfc411db to your computer and use it in GitHub Desktop.

NixOS Root on ZFS (single disk). No swap.

Disable Secure Boot. ZFS modules can not be loaded if Secure Boot is enabled.

Download NixOS Live Image and boot from it.

Connect to the Internet.

Set root password or /root/.ssh/authorized_keys.

Start SSH server:

systemctl restart sshd

Connect from another computer:

ssh root@192.168.1.19

Target disk

List available disks with:

ls /dev/disk/by-id/* If using virtio as disk bus, use /dev/disk/by-path/*.

Declare disk:

DISK='/dev/disk/by-id/nvme-FOO'

Set partition size:

for i in ${DISK}; do
sgdisk --zap-all $i
sgdisk -n1:1M:+1G --set-alignment=4096 -t1:EF00 $i
sgdisk -n2:0:+4G  --set-alignment=4096 -t2:BE00 $i
if test -z $INST_PARTSIZE_RPOOL; then
    sgdisk -n3:0:0 --set-alignment=4096  -t3:BF00 $i
else
    sgdisk -n3:0:+${INST_PARTSIZE_RPOOL}G --set-alignment=4096 -t3:BF00 $i
fi
done

Create boot pool:

zpool create \
    -o compatibility=grub2 \
    -o ashift=12 \
    -o autotrim=on \
    -O acltype=posixacl \
    -O canmount=off \
    -O compression=lz4 \
    -O devices=off \
    -O normalization=formD \
    -O relatime=on \
    -O xattr=sa \
    -O mountpoint=/boot \
    -R /mnt \
    boot \
    ${DISK}-part2

Create root pool:

zpool create \
    -o ashift=12 \
    -o autotrim=on \
    -R /mnt \
    -O xattr=sa \
    -O acltype=posixacl \
    -O canmount=off \
    -O compression=off \
    -O dnodesize=auto \
    -O normalization=formD \
    -O relatime=on \
    -O mountpoint=/ \
    tank \
    ${DISK}-part3

# Root dataset

zfs create \
 -o canmount=off \
 -o mountpoint=none \
                                               tank/nixos

zfs create -o canmount=on -o mountpoint=/      tank/nixos/root

zfs create -o canmount=on -o mountpoint=/home \
 -o encryption=on \
 -o keylocation=prompt \
 -o keyformat=passphrase \
                                               tank/nixos/home

zfs create -o canmount=off -o mountpoint=/var  tank/nixos/var
zfs create -o canmount=on                      tank/nixos/var/lib
zfs create -o canmount=on                      tank/nixos/var/log

zfs create -o canmount=on -o mountpoint=/nix \
  -o atime=off \
  -O relatime=off \
  -O compression=on \
  -O dedup=on \
                                               tank/nixos/nix

# Boot dataset
zfs create -o canmount=off -o mountpoint=none  boot/nixos
zfs create -o canmount=on  -o mountpoint=/boot boot/nixos/root

# Format ESP
for i in ${DISK}; do
 mkfs.vfat -n EFI ${i}-part1
 mkdir -p /mnt/boot/efis/${i##*/}-part1
 mount -t vfat ${i}-part1 /mnt/boot/efis/${i##*/}-part1
done

mkdir -p /mnt/boot/efi
mount -t vfat $(echo $DISK | cut -f1 -d\ )-part1 /mnt/boot/efi

Disable cache, stale cache will prevent system from booting:

mkdir -p /mnt/etc/zfs/
rm -f /mnt/etc/zfs/zpool.cache
touch /mnt/etc/zfs/zpool.cache
chmod a-w /mnt/etc/zfs/zpool.cache
chattr +i /mnt/etc/zfs/zpool.cache

Generate initial system configuration:

nixos-generate-config --root /mnt

Import ZFS-specific configuration:

sed -i "s|./hardware-configuration.nix|./hardware-configuration.nix ./zfs.nix|g" /mnt/etc/nixos/configuration.nix

Configure hostid:

tee -a /mnt/etc/nixos/zfs.nix <<EOF
{ config, pkgs, ... }:

{ boot.supportedFilesystems = [ "zfs" ];
  networking.hostId = "$(head -c 8 /etc/machine-id)";
  boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
EOF

Configure bootloader for both legacy boot and UEFI boot and mirror bootloader:

sed -i '/boot.loader/d' /mnt/etc/nixos/configuration.nix
sed -i '/services.xserver/d' /mnt/etc/nixos/configuration.nix
tee -a /mnt/etc/nixos/zfs.nix <<-'EOF'
boot.loader.efi.efiSysMountPoint = "/boot/efi";
boot.loader.efi.canTouchEfiVariables = false;
boot.loader.generationsDir.copyKernels = true;
boot.loader.grub.efiInstallAsRemovable = true;
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.copyKernels = true;
boot.loader.grub.efiSupport = true;
boot.loader.grub.zfsSupport = true;
boot.loader.grub.extraPrepareConfig = ''
  mkdir -p /boot/efis
  for i in  /boot/efis/*; do mount $i ; done

  mkdir -p /boot/efi
  mount /boot/efi
'';
boot.loader.grub.extraInstallCommands = ''
ESP_MIRROR=$(mktemp -d)
cp -r /boot/efi/EFI $ESP_MIRROR
for i in /boot/efis/*; do
 cp -r $ESP_MIRROR/EFI $i
done
rm -rf $ESP_MIRROR
'';
boot.loader.grub.devices = [
EOF

for i in $DISK; do
  printf "      \"$i\"\n" >>/mnt/etc/nixos/zfs.nix
done

tee -a /mnt/etc/nixos/zfs.nix <<EOF
    ];
EOF

Mount datasets with zfsutil option:

sed -i 's|fsType = "zfs";|fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ];|g' \
/mnt/etc/nixos/hardware-configuration.nix

Set root password:

rootPwd=$(mkpasswd -m SHA-512 -s)

Declare password in configuration:

tee -a /mnt/etc/nixos/zfs.nix <<EOF
users.users.root.initialHashedPassword = "${rootPwd}";
}
EOF

Install system and apply configuration:

nixos-install -v --show-trace --no-root-passwd --root /mnt

Unmount filesystems:

umount -Rl /mnt
zpool export -a

Reboot:

reboot
@osv
Copy link
Author

osv commented Sep 19, 2022

blkdiscard $DISK 2>/dev/null

sgdisk --zap-all                        $DISK

sgdisk -n1:1M:+127M -t1:EF00 -c1:EFI    $DISK
sgdisk -n2:0:+512M  -t2:8300 -c2:Boot   $DISK
sgdisk -n3:0:0      -t3:8309 -c3:Ubuntu $DISK

sgdisk --print                          $DISK

@osv
Copy link
Author

osv commented Apr 28, 2023

  1. Use quota option
-O quota=1.4TB \ # Don't let it use more than about 90% of the space 
  1. Set nvme sector size to 4k: https://www.bjonnh.net/article/20210721_nvme4k/

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