Skip to content

CarlHMitchell/Nixos-Windows-Dual-Boot-Example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Installing NixOS with existing Windows with UEFI boot, root on tmpfs, home on tmpfs, everything else on encrypted ZFS datasets, and a flake-based config

Obviously this takes a lot of RAM. 12GiB for / and /home tmpfs in the defaults provided here. ECC RAM is highly recommended, the chance of error increases as RAM size increases.

This configuration example is for one machine (but should be easy to support more) and one user (with significantly more work to support more, due to a bug set to be solved by this). I expect most people reading this will not need multiple users, but may have multiple machines they want to configure.

If you're installing Windows, it's best if you manually partition your disk so you can have an EFI system partition larger than 100MiB. That allows for more NixOS boot generations to be stored. This config limits to 2 generations.

Disk partitions:
/dev/disk/by-id/ID1 # Some disk name
├─/dev/disk/by-id/ID1-part1 # EFI system partition, Windows makes this 100MiB by default. FAT32, make it bigger if you can.
├─/dev/disk/by-id/ID1-part2 # MSR partition, Microsoft System Reserved
├─/dev/disk/by-id/ID1-part3 # C: for Windows
├─/dev/disk/by-id/ID1-part4 # ZFS root pool named "rpool"
├─/dev/disk/by-id/ID1-part5 # Optional swap partition, not strictly necessary especially if you have tons of RAM
└─/dev/disk/by-id/ID1-part6 # Windows Recovery Partiton, usually around 650MiB, could be deleted

Mount layout:
/ tmpfs
├─/boot /dev/sda1
├─/nix rpool/local/nix
├─/home/persist rpool/safe/home
└─/persist rpool/safe/persist

Everything in rpool/safe is worth backing up. It's the state you've opted in to saving! Everything else can be re-generated if needed.

Note: This config is adapted from https://gist.github.com/byrongibson/b279469f0d2954cc59b3db59c511a199, with updates for current (23.05) NixOS options as needed, tmpfs home, flakes, etc. Unlike that guide, this assumes you already have Windows installed and just shrank the C: partition to provide space for a ZFS partition, and have created an unformatted blank partition there.

Useful commands

mount -l | grep sda
findmnt | grep zfs
lsblk
ncdu -x /
zpool list
zfs list -o name,mounted,mountpoint
zfs mount (only usable with non-legacy datasets)
zfs unmount -a (unmount everything, only usable with non-legacy datasets)
umount -R /mnt (unmount everything in /mnt recursively, required for legacy zfs datasets)
zpool export $POOL (disconnects the pool) NOTE: you MUST export the pool before reboot after running nixos-install.
zpool remove $POOL sda1 (removes the disk from your zpool)
zpool destroy $POOL (this destroys the pool and it's gone and rather difficult to retrieve)

Some ZFS properties cannot be changed after the pool and/or datasets are created. Some discussion on this:
https://www.reddit.com/r/zfs/comments/nsc235/what_are_all_the_properties_that_cant_be_modified/ ashift is one of these properties, but is easy to determine. Use the following commands: disk logical blocksize: $ sudo blockdev --getbsz /dev/sdX (ashift) disk physical blocksize: $ sudo blockdev --getpbsz /dev/sdX (not ashift but interesting)

You'll need to prefix essentially all of the following commands with sudo, so instead just enter a root shell with sudo -i. BE CAREFUL.

Pick your blank partition for ZFS ls -l /dev/disk/by-id, and save its path in a variable.

ZFS=/dev/disk/by-id/YOUR_CHOICE_HERE

Pick the EFI system partiton from ls -l /dev/disk/by-uuid, and save its path in a variable. By-id can work too.

BOOT=/dev/disk/by-uuid/YOUR_CHOICE_HERE

Create the ZFS pool, named "rpool" for this config:

-f force
-m none (mountpoint), canmount=off. ZFS datasets on this pool unmountable unless explicitly specified otherwise in 'zfs create'.
Use blockdev --getbsz /dev/sdX to find correct ashift for your disk.
acltype=posix, xattr=sa required
atime=off and relatime=on for performance
recordsize depends on usage, 16k for database server or similar, 1M for home media server with large files
normalization=formD for max compatility
secondarycache=none to disable L2ARC which is not needed

More info on pool properties:
https://nixos.wiki/wiki/NixOS_on_ZFS#Dataset_Properties
https://jrs-s.net/2018/08/17/zfs-tuning-cheat-sheet/

zpool create -f	-m none	-R /mnt \
    -o ashift=12                \
    -o listsnapshots=on         \
    -O acltype=posix            \
    -O compression=zstd         \
    -O encryption=on            \
    -O keylocation=prompt       \
    -O keyformat=passphrase     \
    -O canmount=off             \
    -O atime=off                \
    -O relatime=on              \
    -O recordsize=1M            \
    -O dnodesize=auto           \
    -O xattr=sa                 \
    -O normalization=formD      \
    -O secondarycachne=none     \
    rpool "${ZFS:?}"

Make ZFS datasets for /nix, /persist, and /home/persist. Datasets under /rpool/safe should be backed up, those under /rpool/local can be trivially re-created.

zfs create -p -v -o secondarycache=none -o mountpoint=legacy rpool/local/nix
zfs create -p -v -o secondarycache=none -o mountpoint=legacy rpool/safe/home
zfs create -p -v -o secondarycache=none -o mountpoint=legacy rpool/safe/persist

Create a reserved ZFS dataset. In case the pool runs out of space (even deletes take some space on copy-on-write filesystems) this can be shrunk or deleted to allow freeing space. Then this can be restored.

zfs create -o refreservation=2G -o primarycache=none -o secondarycache=none -o mountpoint=none rpool/reserved

Enable auto-snapshotting for the safe datasets:

zfs set com.sun:auto-snapshot=true rpool/safe

Make TMPFS, make directories and mount datasets:

mkdir -p /mnt
mount -t tmpfs tmpfs /mnt
mkdir -p /mnt/{nix,home,persist,boot}

Replace USER with your username in the following:

mkdir -p /mnt/home/USER
mount -t tmpfs tmpfs /mnt/home/USER
mkdir -p /mnt/home/persist
mount -t zfs rpool/local/nix /mnt/nix
mount -t zfs rpool/safe/home /mnt/home/persist
mount -t zfs rpool/safe/persist /mnt/persist
mount -t vfat "${BOOT:?}" /mnt/boot

Make persistent subdirectories:

mkdir -p /mnt/persist/etc/{ssh,users,nixos,wireguard,NetworkManager/system-connections}
mkdir -p /mnt/persist/var/{log,lib/bluetooth}
mkdir -p /mnt/etc/nixos
mkdir -p /mnt/var/log

Bind mount the /etc/nixos and /var/log directories:

mount -o bind /mnt/persist/etc/nixos /mnt/etc/nixos
mount -o bind /mnt/persist/var/log /mnt/var/log

If you want to use swap, don't forget to mkswap and swapon that partition.

Rename the host name in the example flake.nix (line 72), the folder name in nixos/hosts/ from nix-win-dual-example to your host name, and the hostName line in the default.nix in that folder on line 86.

Get a host ID and set it on line 87 of that file:

head -c8 /etc/machine-id

Set the boot.zfs.devNodes option on line 17 of that file to match your $ZFS partition.

Set the /boot device path to match your $BOOT partition on line 53.

Get what the generated hardware configuration would contain:

nixos-generate-config --show-hardware-config

Use those results to set the networking interfaces, drivers, and kernel modules in that file.

Configure the rest of the options as noted by TODO: in the sample files.

Copy all the edited sample files over to /mnt/etc/nixos/, so that /mnt/etc/nixos/flake.nix exists, etc. Since it's a bind-mount, it'll be persisted.

Make a password for your user, replacing PASSPHRASE and USERNAME below:

mkpasswd -m yescrypt PASSPHRASE > /mnt/etc/users/USERNAME

Run the install, with your host name instead of HOST_NAME:

nixos-install --root /mnt --flake /mnt/etc/nixos#HOST_NAME

Unmount /mnt:

umount -R /mnt

Export the ZFS pool (THIS IS MANDATORY, FAILURE TO EXPORT WILL PREVENT BOOT):

zpool export rpool

Reboot into your new system!

About

Example config & instructions for setting up NixOS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages