From cc6d72c02db6f36136be3f4b7ae273b8271333a7 Mon Sep 17 00:00:00 2001 From: Maurice Zhou Date: Fri, 10 Feb 2023 16:40:30 +0100 Subject: [PATCH] NixOS Root on ZFS: use Nix-config for multi-disk NixOS has enjoyed popularity among ZFS users thanks to its declarative configuration and native ZFS support. However, the installation guide used hardcoded disk names in configuration files, which is unnecessary and is the source of difficulties in multidisk setups. The guide is now rewritten to leverage expressions in the Nix language to manage multidisk setups. Also adds instruction on replacing failed disk. Closes #385. Signed-off-by: Maurice Zhou --- .../NixOS/Root on ZFS/1-preparation.rst | 15 +- .../Root on ZFS/2-system-installation.rst | 39 ++- .../Root on ZFS/3-system-configuration.rst | 202 +++++++++----- .../Root on ZFS/configuration-immutable.nix | 252 ++++++++++++++++++ .../NixOS/Root on ZFS/configuration.nix | 227 ++++++++++++++++ 5 files changed, 651 insertions(+), 84 deletions(-) create mode 100644 docs/Getting Started/NixOS/Root on ZFS/configuration-immutable.nix create mode 100644 docs/Getting Started/NixOS/Root on ZFS/configuration.nix diff --git a/docs/Getting Started/NixOS/Root on ZFS/1-preparation.rst b/docs/Getting Started/NixOS/Root on ZFS/1-preparation.rst index 0370768..311d860 100644 --- a/docs/Getting Started/NixOS/Root on ZFS/1-preparation.rst +++ b/docs/Getting Started/NixOS/Root on ZFS/1-preparation.rst @@ -8,7 +8,7 @@ Preparation #. Disable Secure Boot. ZFS modules can not be loaded if Secure Boot is enabled. #. Download `NixOS Live Image - `__ and boot from it. + `__ and boot from it. #. Connect to the Internet. #. Set root password or ``/root/.ssh/authorized_keys``. #. Start SSH server:: @@ -17,15 +17,15 @@ Preparation #. Connect from another computer:: - ssh root@192.168.1.19 + ssh root@192.168.1.91 #. Target disk List available disks with:: - ls /dev/disk/by-id/* + find /dev/disk/by-id/ - If using virtio as disk bus, use ``/dev/disk/by-path/*``. + If using virtio as disk bus, use ``/dev/disk/by-path/``. Declare disk array:: @@ -37,11 +37,10 @@ Preparation #. Set partition size: - Set swap size. It's `recommended `__ - to setup a swap partition. If you intend to use hibernation, - the minimum should be no less than RAM size. Skip if swap is not needed:: + Set swap size, set to 1 if you don't want swap to + take up too much space:: - INST_PARTSIZE_SWAP=8 + INST_PARTSIZE_SWAP=4 Root pool size, use all remaining disk space if not set:: diff --git a/docs/Getting Started/NixOS/Root on ZFS/2-system-installation.rst b/docs/Getting Started/NixOS/Root on ZFS/2-system-installation.rst index 0c3ad69..4d6f3eb 100644 --- a/docs/Getting Started/NixOS/Root on ZFS/2-system-installation.rst +++ b/docs/Getting Started/NixOS/Root on ZFS/2-system-installation.rst @@ -16,7 +16,7 @@ System Installation sgdisk -n2:0:+4G -t2:BE00 $i - test -z $INST_PARTSIZE_SWAP || sgdisk -n4:0:+${INST_PARTSIZE_SWAP}G -t4:8200 $i + sgdisk -n4:0:+${INST_PARTSIZE_SWAP}G -t4:8200 $i if test -z $INST_PARTSIZE_RPOOL; then sgdisk -n3:0:0 -t3:BF00 $i @@ -25,6 +25,12 @@ System Installation fi sgdisk -a1 -n5:24K:+1000K -t5:EF02 $i + + sync && udevadm settle && sleep 3 + + cryptsetup open --type plain --key-file /dev/random $i-part4 ${i##*/}-part4 + mkswap /dev/mapper/${i##*/}-part4 + swapon /dev/mapper/${i##*/}-part4 done #. Create boot pool:: @@ -84,9 +90,7 @@ System Installation If not using a multi-disk setup, remove ``mirror``. -#. This section implements dataset layout as described in `overview <1-preparation.html>`__. - - Create root system container: +#. Create root system container: - Unencrypted:: @@ -108,18 +112,25 @@ System Installation -o keyformat=passphrase \ rpool/nixos - Create system datasets:: + You can automate this step (insecure) with: ``echo POOLPASS | zfs create ...``. - zfs create -o canmount=on -o mountpoint=/ rpool/nixos/root - zfs create -o canmount=on -o mountpoint=/home rpool/nixos/home - zfs create -o canmount=off -o mountpoint=/var rpool/nixos/var - zfs create -o canmount=on rpool/nixos/var/lib - zfs create -o canmount=on rpool/nixos/var/log + Create system datasets, let NixOS declaratively + manage mountpoints with ``mountpoint=legacy``:: - Create boot dataset:: - - zfs create -o canmount=off -o mountpoint=none bpool/nixos - zfs create -o canmount=on -o mountpoint=/boot bpool/nixos/root + zfs create -o mountpoint=legacy rpool/nixos/root + mount -t zfs rpool/nixos/root /mnt/ + zfs create -o mountpoint=legacy rpool/nixos/home + mkdir /mnt/home + mount -t zfs rpool/nixos/home /mnt/home + zfs create -o mountpoint=legacy rpool/nixos/var + zfs create -o mountpoint=legacy rpool/nixos/var/lib + zfs create -o mountpoint=legacy rpool/nixos/var/log + zfs create -o mountpoint=none bpool/nixos + zfs create -o mountpoint=legacy bpool/nixos/root + mkdir /mnt/boot + mount -t zfs bpool/nixos/root /mnt/boot + zfs create -o mountpoint=legacy rpool/nixos/empty + zfs snapshot rpool/nixos/empty@start #. Format and mount ESP:: diff --git a/docs/Getting Started/NixOS/Root on ZFS/3-system-configuration.rst b/docs/Getting Started/NixOS/Root on ZFS/3-system-configuration.rst index e969d0a..7bbb28f 100644 --- a/docs/Getting Started/NixOS/Root on ZFS/3-system-configuration.rst +++ b/docs/Getting Started/NixOS/Root on ZFS/3-system-configuration.rst @@ -6,83 +6,48 @@ System Configuration .. contents:: Table of Contents :local: -#. Generate initial system configuration:: +#. Download system configuration from this repo:: - nixos-generate-config --root /mnt + mkdir -p /mnt/etc/nixos/ + curl -o /mnt/etc/nixos/configuration.nix -L \ + https://github.com/openzfs/openzfs-docs/raw/master/docs/Getting%20Started/NixOS/Root%20on%20ZFS/configuration.nix -#. Import ZFS-specific configuration:: +#. Customize configuration to your hardware:: - sed -i "s|./hardware-configuration.nix|./hardware-configuration.nix ./zfs.nix|g" /mnt/etc/nixos/configuration.nix + for i in $DISK; do + sed -i \ + "s|PLACEHOLDER_FOR_DEV_NODE_PATH|\"${i%/*}/\"|" \ + /mnt/etc/nixos/configuration.nix + break + done -#. Configure hostid:: - - tee -a /mnt/etc/nixos/zfs.nix <>/mnt/etc/nixos/zfs.nix - done - - tee -a /mnt/etc/nixos/zfs.nix <`__. + Apply your own hardware configuration in this file, + then execute:: + + nixos-rebuild boot + + Then reboot. You may need to make certain + adjustments to where configuration files are stored, + see `NixOS wiki `__ for + details. + +Replace a failed disk +===================== + +When a disk fails in a mirrored setup, the disk can be +replaced with the following procedure. + +#. Shutdown the computer. + +#. Replace the failed disk with another disk. The + replacement should be at least the same size or + larger than the failed disk. + +#. Boot the computer. When a disk fails, the system will boot, albeit + several minutes slower than normal. This is due to + the initrd and systemd designed to only import a pool + in degraded state after a 90s timeout. Swap + partition on that disk will also fail. + +#. Launch a ephemeral nix shell with gptfdisk:: + + nix-shell -p gptfdisk + +#. Identify the bad disk and a working old disk:: + + ZPOOL_VDEV_NAME_PATH=1 zpool status + + pool: bpool + status: DEGRADED + action: Replace the device using 'zpool replace'. + ... + config: bpool + mirror-0 + 2387489723748 UNAVAIL 0 0 0 was /dev/disk/by-id/ata-BAD-part2 + /dev/disk/by-id/ata-OLD-part2 ONLINE 0 0 0 + +#. Store the bad disk and a working old disk in a variable, omit the partition number ``-partN``:: + + BAD=/dev/disk/by-id/ata-BAD + OLD=/dev/disk/by-id/ata-OLD + +#. Identify the new disk:: + + find /dev/disk/by-id/ + + /dev/disk/by-id/ata-OLD-part1 + /dev/disk/by-id/ata-OLD-part2 + ... + /dev/disk/by-id/ata-OLD-part5 + /dev/disk/by-id/ata-NEW <-- new disk w/o partition table + +#. Store the new disk in a variable:: + + NEW=/dev/disk/by-id/ata-NEW + +#. Replicate partition table on the new disk:: + + sgdisk -Z $NEW + sgdisk --backup=backup $OLD + sgdisk --load-backup=backup $NEW + sgdisk --randomize-guids $NEW + +#. If the new disk is larger than the old disk, expand root pool partition size:: + + sgdisk --delete=3 $NEW + + # expand to all remaining disk space + sgdisk -n3:0:0 -t3:BF00 $NEW + + Note that this space will only become available once all disks in the mirrored pool are + replaced with larger disks. + +#. Format and mount EFI system partition:: + + mkfs.vfat -n EFI ${NEW}-part1 + mkdir -p /boot/efis/${NEW##*/}-part1 + mount -t vfat ${NEW}-part1 /boot/efis/${NEW##*/}-part1 + +#. Replace failed disk in pool:: + + zpool offline bpool ${BAD}-part2 + zpool offline rpool ${BAD}-part3 + zpool replace bpool ${BAD}-part2 ${NEW}-part2 + zpool replace rpool ${BAD}-part3 ${NEW}-part3 + zpool online bpool ${NEW}-part2 + zpool online rpool ${NEW}-part3 + + Let the new disk resilver. Check status with ``zpool status``. + +#. Update NixOS system configuration:: + + sed -i "s|${BAD##*/}|${NEW##*/}|" /etc/nixos/machine.nix + +#. Apply the updated NixOS system configuration, reinstall bootloader, then reboot:: + + nixos-rebuild boot --install-bootloader + + reboot diff --git a/docs/Getting Started/NixOS/Root on ZFS/configuration-immutable.nix b/docs/Getting Started/NixOS/Root on ZFS/configuration-immutable.nix new file mode 100644 index 0000000..e696d0d --- /dev/null +++ b/docs/Getting Started/NixOS/Root on ZFS/configuration-immutable.nix @@ -0,0 +1,252 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). +{ config, lib, pkgs, modulesPath, ... }: + +let + zfsRoot.partitionScheme = { + biosBoot = "-part5"; + efiBoot = "-part1"; + swap = "-part4"; + bootPool = "-part2"; + rootPool = "-part3"; + }; + zfsRoot.devNodes = PLACEHOLDER_FOR_DEV_NODE_PATH; # MUST have trailing slash! /dev/disk/by-id/ + zfsRoot.bootDevices = (import ./machine.nix).bootDevices; + zfsRoot.mirroredEfi = "/boot/efis/"; + +in { + # adjust according to your platform, such as + imports = [ + # (modulesPath + "/profiles/qemu-guest.nix") + # (modulesPath + "/profiles/all-hardware.nix") + (modulesPath + "/installer/scan/not-detected.nix") + ]; + systemd.services.zfs-mount.enable = false; + + # networking.hostName = "nixos"; # Define your hostname. + # Pick only one of the below networking options. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + # networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. + + # Set your time zone. + # time.timeZone = "Europe/Amsterdam"; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + # i18n.defaultLocale = "en_US.UTF-8"; + # console = { + # font = "Lat2-Terminus16"; + # keyMap = "us"; + # useXkbConfig = true; # use xkbOptions in tty. + # }; + + # Enable the X11 windowing system. + + # Configure keymap in X11 + # "eurosign:e"; + # "caps:escape" # map caps to escape. + # }; + + # Enable CUPS to print documents. + # services.printing.enable = true; + + # Enable sound. + # sound.enable = true; + # hardware.pulseaudio.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + + # Define a user account. Don't forget to set a password with ‘passwd’. + # users.users.alice = { + # isNormalUser = true; + # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + # packages = with pkgs; [ + # firefox + # thunderbird + # ]; + # }; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; + [ + # vim + ## Do not forget to add an editor to edit configuration.nix! + ## The Nano editor is also installed by default. + # wget + mg + ]; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + # Enable the OpenSSH daemon. + services.openssh.enable = false; + users.users.root = { + ##hash: mkpasswd -m SHA-512 -s + initialHashedPassword = PLACEHOLDER_FOR_ROOT_PWD_HASH; + openssh.authorizedKeys.keys = [ + ]; + }; + programs.git.enable = true; + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + # Copy the NixOS configuration file and link it from the resulting system + # (/run/current-system/configuration.nix). This is useful in case you + # accidentally delete configuration.nix. + system.copySystemConfiguration = true; + + # 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? + + boot.initrd.availableKernelModules = [ + "ahci" + "xhci_pci" + "virtio_pci" + "virtio_blk" + "ehci_pci" + "nvme" + "uas" + "sd_mod" + "sr_mod" + "sdhci_pci" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems = { + "/" = { + device = "rpool/nixos/empty"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/oldroot" = { + device = "rpool/nixos/root"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + neededForBoot = true; + }; + + "/nix" = { + device = "/oldroot/nix"; + fsType = "none"; + options = [ "bind" "X-mount.mkdir" ]; + }; + + "/etc/nixos" = { + device = "/oldroot/etc/nixos"; + fsType = "none"; + options = [ "bind" "X-mount.mkdir" ]; + }; + + "/home" = { + device = "rpool/nixos/home"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/var/lib" = { + device = "rpool/nixos/var/lib"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/var/log" = { + device = "rpool/nixos/var/log"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/boot" = { + device = "bpool/nixos/root"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + } // (builtins.listToAttrs (map (diskName: { + name = zfsRoot.mirroredEfi + diskName + zfsRoot.partitionScheme.efiBoot; + value = { + device = zfsRoot.devNodes + diskName + zfsRoot.partitionScheme.efiBoot; + fsType = "vfat"; + options = [ + "x-systemd.idle-timeout=1min" + "x-systemd.automount" + "noauto" + "nofail" + ]; + }; + }) zfsRoot.bootDevices)); + + swapDevices = (map (diskName: { + device = zfsRoot.devNodes + diskName + zfsRoot.partitionScheme.swap; + discardPolicy = "both"; + randomEncryption = { + enable = true; + allowDiscards = true; + }; + }) zfsRoot.bootDevices); + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.networkmanager.enable = true; + # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; + hardware.cpu.amd.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; + + + boot.supportedFilesystems = [ "zfs" ]; + networking.hostId = "abcd1234"; + boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; + boot.loader.efi.efiSysMountPoint = with builtins; + (zfsRoot.mirroredEfi + (head zfsRoot.bootDevices) + zfsRoot.partitionScheme.efiBoot); + boot.zfs.devNodes = zfsRoot.devNodes; + 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.extraInstallCommands = with builtins; + (toString (map (diskName: + "cp -r " + config.boot.loader.efi.efiSysMountPoint + "/EFI" + " " + + zfsRoot.mirroredEfi + diskName + zfsRoot.partitionScheme.efiBoot + "\n") + (tail zfsRoot.bootDevices))); + boot.loader.grub.devices = + (map (diskName: zfsRoot.devNodes + diskName) zfsRoot.bootDevices); + boot.initrd.postDeviceCommands = '' + zpool import -Nf rpool + zfs rollback -r rpool/nixos/empty@start + zpool export -a + ''; +} + diff --git a/docs/Getting Started/NixOS/Root on ZFS/configuration.nix b/docs/Getting Started/NixOS/Root on ZFS/configuration.nix new file mode 100644 index 0000000..18a9d4b --- /dev/null +++ b/docs/Getting Started/NixOS/Root on ZFS/configuration.nix @@ -0,0 +1,227 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). +{ config, lib, pkgs, modulesPath, ... }: + +let + zfsRoot.partitionScheme = { + biosBoot = "-part5"; + efiBoot = "-part1"; + swap = "-part4"; + bootPool = "-part2"; + rootPool = "-part3"; + }; + zfsRoot.devNodes = PLACEHOLDER_FOR_DEV_NODE_PATH; # MUST have trailing slash! /dev/disk/by-id/ + zfsRoot.bootDevices = (import ./machine.nix).bootDevices; + zfsRoot.mirroredEfi = "/boot/efis/"; + +in { + # adjust according to your platform, such as + imports = [ + # (modulesPath + "/profiles/qemu-guest.nix") + # (modulesPath + "/profiles/all-hardware.nix") + (modulesPath + "/installer/scan/not-detected.nix") + ]; + systemd.services.zfs-mount.enable = false; + + # networking.hostName = "nixos"; # Define your hostname. + # Pick only one of the below networking options. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + # networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. + + # Set your time zone. + # time.timeZone = "Europe/Amsterdam"; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + # i18n.defaultLocale = "en_US.UTF-8"; + # console = { + # font = "Lat2-Terminus16"; + # keyMap = "us"; + # useXkbConfig = true; # use xkbOptions in tty. + # }; + + # Enable the X11 windowing system. + + # Configure keymap in X11 + # "eurosign:e"; + # "caps:escape" # map caps to escape. + # }; + + # Enable CUPS to print documents. + # services.printing.enable = true; + + # Enable sound. + # sound.enable = true; + # hardware.pulseaudio.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + + # Define a user account. Don't forget to set a password with ‘passwd’. + # users.users.alice = { + # isNormalUser = true; + # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + # packages = with pkgs; [ + # firefox + # thunderbird + # ]; + # }; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; + [ + # vim + ## Do not forget to add an editor to edit configuration.nix! + ## The Nano editor is also installed by default. + # wget + mg + ]; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + # Enable the OpenSSH daemon. + services.openssh.enable = false; + users.users.root = { + ##hash: mkpasswd -m SHA-512 -s + initialHashedPassword = PLACEHOLDER_FOR_ROOT_PWD_HASH; + openssh.authorizedKeys.keys = [ + ]; + }; + programs.git.enable = true; + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + # Copy the NixOS configuration file and link it from the resulting system + # (/run/current-system/configuration.nix). This is useful in case you + # accidentally delete configuration.nix. + system.copySystemConfiguration = true; + + # 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? + + boot.initrd.availableKernelModules = [ + "ahci" + "xhci_pci" + "virtio_pci" + "virtio_blk" + "ehci_pci" + "nvme" + "uas" + "sd_mod" + "sr_mod" + "sdhci_pci" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems = { + "/" = { + device = "rpool/nixos/root"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/home" = { + device = "rpool/nixos/home"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/var/lib" = { + device = "rpool/nixos/var/lib"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/var/log" = { + device = "rpool/nixos/var/log"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + + "/boot" = { + device = "bpool/nixos/root"; + fsType = "zfs"; + options = [ "X-mount.mkdir" ]; + }; + } // (builtins.listToAttrs (map (diskName: { + name = zfsRoot.mirroredEfi + diskName + zfsRoot.partitionScheme.efiBoot; + value = { + device = zfsRoot.devNodes + diskName + zfsRoot.partitionScheme.efiBoot; + fsType = "vfat"; + options = [ + "x-systemd.idle-timeout=1min" + "x-systemd.automount" + "noauto" + "nofail" + ]; + }; + }) zfsRoot.bootDevices)); + + swapDevices = (map (diskName: { + device = zfsRoot.devNodes + diskName + zfsRoot.partitionScheme.swap; + discardPolicy = "both"; + randomEncryption = { + enable = true; + allowDiscards = true; + }; + }) zfsRoot.bootDevices); + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.networkmanager.enable = true; + # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; + hardware.cpu.amd.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; + + boot.supportedFilesystems = [ "zfs" ]; + networking.hostId = "abcd1234"; + boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; + boot.loader.efi.efiSysMountPoint = with builtins; + (zfsRoot.mirroredEfi + (head zfsRoot.bootDevices) + zfsRoot.partitionScheme.efiBoot); + boot.zfs.devNodes = zfsRoot.devNodes; + 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.extraInstallCommands = with builtins; + (toString (map (diskName: + "cp -r " + config.boot.loader.efi.efiSysMountPoint + "/EFI" + " " + + zfsRoot.mirroredEfi + diskName + zfsRoot.partitionScheme.efiBoot + "\n") + (tail zfsRoot.bootDevices))); + boot.loader.grub.devices = + (map (diskName: zfsRoot.devNodes + diskName) zfsRoot.bootDevices); +} +