Published on

How to Dual Boot NixOS and Windows

This post is part of a series about NixOS. The series is done in support of my upcoming Opus Magnum, "Practical NixOS: the Book", and you can read the detailed post here.

Should you find this article lacking some information, be sure to tell me in the comments or elsewhere, since it is a living document and I am very keen on expanding it.

Table of Contents (Click to Hide/Show)

Bottom Line Up

This article is applicable for any version of Windows, incl. Windows 10 and Windows 11.

Dualbooting NixOS and Windows is not a complex task. First, you'll use Windows to shrink its partition and make space for NixOS, and to flash a NixOS image to a USB drive. You would then install NixOS, and use GParted on NixOS to increase the Boot partition size.

You'll (obviously) need a PC that comes with Windows already, and a flash USB drive - and don't worry if it's already used for data storage.

It's easier to install NixOS after Windows (and in general, it's easier to install Linux after Windows). If you hadn't installed either of systems yet - install Windows first, and consider making Windows' Boot partition larger - 4 GiB, and not 100 MiB that comes from the default partitioning schema, otherwise you'll have to do some manual adjustments after.

A thing that drastically improves the NixOS installation experience is the GUI installer called Calamares, which was introduced in 2022. It simplifies initial configuration, and more importantly, unlike the pure terminal experience - it gives the confidence in the success of the installation process. I personally would still prefer CLI to a GUI though.

As I've mentioned - the only caveat to be aware of is the default 100 MiB Windows Boot partition. Due to the way the NixOS' bootloader - Systemd Boot - shares the same Boot partition and utilizes it to store Linux kernels, its size is insufficient, so you'll have to copy its contents, resize it, and then manually re-create it. It will require some usage of terminal interface, but is also rather straight-forward.

This article not only explains the sequence of steps to be performed, but also touches on the Why's of the things being they are, dives deeper to explain each step in thorough detail, and supplies enough context - regarding the ecosystem, historical, etc.

In the end, you will get a full solution, and not something you would have to tweak afterwards.

Resizing Windows Parition to Fit NixOS

Assuming you already have Windows 11 installed, our first step would be to shrink the Windows partition to make some space for NixOS.

In my initial layout, the laptop has a single 1 TB SSD fully occupied by Windows 11.

Go to the start menu, search for "disk management", or just open up a PowerShell and type in diskmgmt.msc, then hit Enter.

Disk Management Application

You should see Disk 0 with 3 paritions:

  • EFI boot partition
  • Primary NTFS partition containing Windows. The letter may be different for you, but most likely it would be "Disk C:".
  • Recovery partition

Right-click the "Disk C:" partition, and choose "Shrink Volume...". We're going to give NixOS 400000 MBs (400 GBs), so slightly less than a half of the Drive's total size. Click "Shrink":

Partition Sizing

This will create unallocated free space:

Unallocated Free Space

In the end, we got 390.63 GB of free space instead of 400, because Windows cannot properly distinguish GBs from GiBs, where Gigabyte is a marketing slang to sell you less storage for more money, while Gibibyte is a proper unit of measurement. Whatever.

One out of two steps within Windows is complete! Up to the next.

Flashing NixOS Image to USB Drive

We're going to use an application called Ventoy to "burn" the NixOS Image to the USB drive.

Ventoy is a marvel of FOSS software engineering, no more, no less. It allows you to load an image onto a USB Drive in a unique way. Unlike traditional methods, it allows the USB Drive to remain usable as a general writeable storage, without turning it into a read-only device. Your cat pics and family photos can resize alongside NixOS bootable image. Not only that - with Ventoy, you can easily add multiple ISO images to the drive by simply copying them, not burning. This functionality lets you boot these images directly when starting your computer. According to the Ventoy website, they have successfully tested this feature with over 1100 different operating systems and utility images.

Green Dragon
Green Dragon forewarns:
"
A marvel of engineering, it also contains binary blobs within its source. If you don't trust the developer, use something else - e.g. Balena Etcher, Rufus, or the old-fashioned dd.
"

Go to Ventoy and grab a zip-file called ventoy-X.X.XXX-windows.zip at the Downloads Page.

Right-click the downloaded ventoy-X.X.XXX-windows.zip and unpack it. Run Ventoy2Disk.exe, select the device and press the "Install" button.

The process is straight-forward, so after the USB flashing is done, your drive should be called "Ventoy", and feature the ventoy firmware.

Ventoy Before and After

Now, let's grab the latest NixOS stable image (24.05 at the time of writing) with Calamares GUI Installer featuring Gnome Desktop Environment. Go to the Downloads Page and you'll find it there, or use a direct link (x86_64). At the Downloads Page, you are also given a choice to download the installer with KDE Plasma Desktop Environment. It doesn't matter much which you choose, since this Desktop Environment is used just to enable the graphics of the NixOS Live USB system with Calamares Installer, while the Installer itself gives you flexibility to select almost any DE of your liking. Currently, GNOME, KDE Plasma, Xfce, Pantheon, Cinnamon, MATE, Enlightenment, LXQt, Budgie, and no-desktop pure terminal could be chosen from the installer menu, and many-many more are available via NixOS options and from nixpkgs after you've completed the installation.

After the download is finished, just copy the ISO image to the Ventoy drive and reboot the PC.

Installing NixOS with Graphical Installer

Immediately after restarting the PC you should enter the boot menu. Look up on google, how to do so. Most likely you should press or hold some F-key or Delete. If you see Windows login prompt instead of boot menu, restart your PC once again.

In the boot menu, use arrows to select the USB Drive and press Enter. If the flash drive contains multiple bootable ISO images, all of these would be listed in the Ventoy menu. Select nixos-gnome-24.05-XXX-x86_64-linux.iso, then press Boot in normal mod, and finally press NixOS 24.05.XXX Installer to boot into NixOS Live USB.

The installer greets you:

NixOS Gnome Startup

Use wired connection or WiFi to connect to the internet. WiFi drivers should already be available on the Live USB system, so to connect to the WiFi, click the battery and sound icons in top-right corner, then select your network SSID (name) and enter the password.

You may not proceed with the installation unless you make that "Network not available" message go away. After establishing the connection, press "Cancel" in the installer window and confirm your choice. Then click the pill and three dots menu in the top-left corner, look the to the bottom, and click the Orange Square to open up the Installer again.

Titan
Titan laments:
"
Even for a mere installer, GNOME's interface, user experience, and choice of icongraphy are rather poor, extremely poor even, I regret it was selected for the article - should have gone with KDE or Xfce instead.
"

The Calamares Installer's prompt will walk you through the selections of locales, keyboards, users settings, and desktop environment.

At the "Unfree Software" tab, enable the "Allow unfree software", because you would most likely want to install some, e.g. the VSCode editor.

At the "Partitions" tab, you may select the "Replace a partition" radio button, and then click on the grey bar representing free space. That would create one primary partition, and one Boot partition. Instead of selecting "Replace a partition" I suggest you to go with the "Manual partitioning" option to also create a swap partition.

At the "Partitions" tab, select the "Replace a partition" radio button, and then click on the grey bar representing the unallocated space we got from shrinking the Windows partition. That would create one primary partition, and one Boot partition.

You may also go with "Manual partitioning" for a more sophisticated setup - for exaple, you may also want to have a swap partition, even though such a partition is of little use on modern systems with sufficient RAM.

Click next to go to the "Summary", ensure that you're satisfied with the chosed settings, and the proceed to finalize the installation process to persist NixOS with the initial configuration.

You now have a working NixOS system which you may use, however it may prove troublesome very soon, unless you increase the Boot partition size.

Increasing Boot Partition Size

Now, we are ought to increase the Boot partition size. Unfortunately, GParted does not support resizing FAT32 partitions, thus we would have to copy its contents, delete it, then recreate with bigger size, and finally copy the contents back (after all, the contents are just files.)

Expect a lot of screenshots below, as I wanted to capture every step in full detail.

Setting Up the Partition Layout

Here's the initial alignment when you open GParted:

GParted Initial Alignment

First, we would have to shrink the Windows 11 partition. Shrinking it from beginning (creating leading unallocated space) is exactly the same procedure as shrinking it from the end, but may take slightly more time, since files that may already occupy starting space would have to be moved (copied) "further" up the disk.

Right-click the Windows 11 partion (/dev/nvme0n1p3 with ntfs file system) and select "Resize". The following prompt will pop up:

GParted Initial Alignment Windows 11 Partition

Be not afraid of the jumpscare warning below. Moving (resizing a partition from the beginning) is a safe operation. Be sure that your laptop is plugged into power, you probably don't want it to shutdown mid-process. Having backups in advance also helps, but, to be honest - you don't have to worry about it. I've done this multiple times without a backup (shame on me), and all such operations were successful and without any issues.

GParted Wargning for Windows 11 Partition

GParted interface is smart enough to figure out that if you increase the preceeding space, some other space has to be shrinked. You won't have to reach for a calculator to calculate all the values manually. Just enter the size of the space you want to de-allocate - in our case, the Boot partition is 100 MiB, and we want it to be 4 GiB = 4096 MiB, thus we need to add extra 4096-100 = 3996 MiB. Type in 3996 and press enter - GParted will then deduct the same amount from the size of partition ("New size"):

GParted Windows 11 Partition and Resize and Move

Ensure that the partiton is indeed of the desired size and has proper offset, and press enter.

Since there's one more partition sitting in-between Boot and Windows' main partition, we would have to offset it (not resize) by the same amount of 3996 MiB. This 16 MiB "Microsoft reserved partition" is the Recovery partition (briefly mentioned above), it's a legacy Windows thing, so do think about it too much. Right-click the partition (/dev/nvme0n1p2 with unknown file system) and select resize. You'll see that it has 0 MiB of "Free space preceeding" and 3996 of "Free space following", let's swap these.

GParted Initial Reserved Partition

Type 3966 in place of zero, press enter. Again, GParted would be smart enough to shrink unallocated following free space to zero.

GParted Reserved Partition After Move

Finally, let's make Boot partition (EFI system partition /dev/nvme0n1p1 with fat32) 4 GiB (4096 MiB) big:

GParted Initial Boot Partition

Type 0 in place of 3996, press enter - "New size" automatically becomes 4096, just as planned!

GParted Boot Partition After Resize

The layout before applying the operation then would look like this:

GParted Alignment That is to Be Applied

Press the green check mark to proceed with the operation. Applying new schema would take slightly longer than creating partitions from scratch or shrinking partitons from their end - GParted has to move the whole Windows - not the smallest of them! - "further" up the disk. The suggested estimate (10 minutes here) was close to the actual timing, and you should also expect some sensible number (10 to 30 minutes), depending on your disk's speed.

GParted Process of Applying New Schema

Oh no! It has completed 2 out of 3 operations and failed to grow FAT32! Well, that was totally expected - FAT32 is tons of legacy code, and noone in their right mind would want to jump into it to add suport for resizing FAT32 for non-windows tools. As you see, "GNU Parted cannot resize this partition to this size. We're working on it!".

Master Gremlin
Master Gremlin knows:
"
The "working on it" part is a lie - https://bugzilla.gnome.org/show_bug.cgi?id=649324.
"
GParted Failure to Apply FAT32 Resize

2 other operations (shrinking Windows partition and moving the Windows recovery one) were actually successful, so let's delete and recreate the Boot.

GParted Schema After Application with Warning for FAT32

You may also double-click the Boot partition, which now sports a warning triangle - the popup will tell you that most of the space on that partition is unallocated and you may fix it by using "Partition --> Check". This is also a lie.

GParted FAT32 Info

Back-up Files from Boot Partition

Let's start backing-up the files from the Boot partition. You can use a temporary folder on the system. Or you may already have a backup in place, if you're that kind of person.

Since we're using a LiveUSB Installer Image, your active system currently has no access to the file system of the Boot partition - the Boot partition is not "mounted". To access the files, we have to "mount" the partition - create a folder on current system (the folder is not going to be persisted after reboot), and then instruct the Operating System to show files from the Boot partition as if they have belonged inside this folder.

To create a temporary folder, open up the terminal and run the following:

[root@nixos:/home/nixos]# mkdir /tmp/backed-up-boot
[root@nixos:/home/nixos]# mkdir /mnt/actual-boot
[root@nixos:/home/nixos]# mount /dev/nvme0n1p1 /mnt/actual-boot

The first and second commands will create backed-up-boot directory under /tmp directory, and actual-boot directory under /mnt directory. Both /tmp and /mnt directories, standing for "temporary" and "mount" respectively, are already present on virtually every Linux system, incl. NixOS. This choice of parent folders is somewhat arbitrary, as we could have created our directories under any other. But for the purposes of following the convention, the to be mounted Boot partition is going to reside under /mnt, while its contents are going to be backed-up to a directory under /tmp.

Master Gremlin
Master Gremlin injects:
"
An ephemeral copy used just once, that's a 'backup', uh-huh.
"

The third command instructs the system to list all the files on the Boot partition inside the newly-created actual-boot directory, and make them available for read-write operations.

/dev here stands for "device" - and it lists all the devices of this computer. Since Linux, being a derivative of Unix, conisders every thing in existence a file (a stream of bytes to be precise), disks, as well as partitions are listed under this directory, and exposed for different manipulations as files.

Titan
Titan shows off:
"
You've heard it here first: in NixOS, everything is a path. Not specifically a file path.
"

For illustrative purposes, let us now list the files on the Boot partition. For that, we'll use the tree command. tree is not available on the Installer Image, so we have to install it first:

[root@nixos:/home/nixos]# nix-shell -p tree

After installation, running tree /mnt/actual-boot will produce roughly the following structure (some files like additional translations and fonts are omitted):

[nix-shell:/home/nixos]# tree /mnt/boot-to-copy/
/mnt/boot-to-copy/
├── EFI
│   ├── Boot
│   │   └── bootx64.efi
│   ├── Lenovo
│   │   └── BIOS
│   │       └── SelfHealing.fd
│   ├── Linux
│   ├── Microsoft
│   │   ├── Boot
│   │   │   ├── BCD
│   │   │   ├── BCD.LOG
│   │   │   ├── BCD.LOG1
│   │   │   ├── BCD.LOG2
│   │   │   ├── bg-BG
│   │   │   │   ├── bootmgfw.efi.mui
│   │   │   │   └── bootmgr.efi.mui
│   │   │   ├── bootmgfw.efi
│   │   │   ├── bootmgr.efi
│   │   │   ├── BOOTSTAT.DAT
│   │   │   ├── boot.stl
│   │   │   ├── CIPolicies
│   │   │   │   └── Active
│   │   │   │       ├── {5DAC656C-21AD-4A02-AB49-649917162E70}.cip
│   │   │   │       ├── {82443e1e-8a39-4b4a-96a8-f40ddc00b9f3}.cip
│   │   │   │       └── {CDD5CB55-DB68-4D71-AA38-3DF2B6473A52}.cip
│   │   │   ├── cs-CZ
│   │   │   │   ├── bootmgfw.efi.mui
│   │   │   │   ├── bootmgr.efi.mui
│   │   │   │   └── memtest.efi.mui
│   │   │   ├── fi-FI
│   │   │   │   ├── bootmgfw.efi.mui
│   │   │   │   ├── bootmgr.efi.mui
│   │   │   │   └── memtest.efi.mui
│   │   │   ├── Fonts
│   │   │   │   ├── chs_boot.ttf
│   │   │   │   └── wgl4_boot.ttf
│   │   │   ├── kd_02_10df.dll
│   │   │   ├── kd_0C_8086.dll
│   │   │   ├── kdnet_uart16550.dll
│   │   │   ├── kdstub.dll
│   │   │   ├── memtest.efi
│   │   │   ├── qps-ploc
│   │   │   │   └── memtest.efi.mui
│   │   │   ├── Resources
│   │   │   │   ├── bootres.dll
│   │   │   │   └── en-US
│   │   │   │       └── bootres.dll.mui
│   │   │   └── winsipolicy.p7b
│   │   └── Recovery
│   │       ├── BCD
│   │       ├── BCD.LOG
│   │       ├── BCD.LOG1
│   │       └── BCD.LOG2
│   ├── nixos
│   │   ├── icjw0k1sqbikanjm4hl1chp9cpzdn4sv-linux-6.8.2-bzImage.efi
│   │   └── mq9bchqh2czf9dxgfawiy4ag2d75qp7x-initrd-linux-6.8.2-initrd.efi
│   └── systemd
│       └── systemd-bootx64.efi
├── loader
│   ├── entries
│   │   ├── nixos-generation-1.conf
│   ├── entries.srel
│   ├── loader.conf
│   └── random-seed
└── System Volume Information

55 directories, 149 files

To copy the files, run the following:

[nix-shell:/home/nixos]# rsync -ah --progress /mnt/actual-boot /tmp/backed-up-boot

I use rsync -ah --progress command instead of cp, because it can show the operation's progress.

Finally, unmount the Boot partition to "disassociate" it from the running system and enable for partition-related operations with GParted (mind the spelling of umount without n):

[nix-shell:/home/nixos]# umount /tmp/actual-boot

Now, we're ready to recrate the Boot partion.

Recreate the Boot Partition

Right-click the Boot partition and mark it for deletion (it won't delete it yet). Unallocated space will appear in its place:

GParted Unallocated in Place of First Partition

Double click the unallocated space to create the new partition. Allocate the entire space of 4096 MiB. You may end up with occasional 1 or 2 MiBs of unallocated space in-between some partitions due to how the data is physically aligned on the disk, but don't worry about it. Ensure, that you have created the partition as Primary Partiton, fat32 file system, and named it EFI System Partition:

GParted New FAT32

The layout to be applied with new partition will then look like this:

GParted Schema with New FAT32

Click the green check mark to start the creation of new partition.

GParted Creation of New FAT32

Voila! Schema is applied, and the green check mark is greyed-out, since no more operations are pending:

GParted Proper Schema

An important step after applying the schema - since Boot is a special purpose partition, we have to mark it accordingly with proper partition flags. Right-click the Boot partition, select "Flags", and ensure that you have enabled 3 flags - boot, esp, and no_automount. This operation does not require to be applied, so flags persist once you click "Close":

GParted FAT32 Flags

Copy Files Back to Boot and Update NixOS Configuration

The only thing remaining is to restore the files and to instruct NixOS to consider the newly-created partition as part of its configuration.

Remount the Boot partition and copy files back to it:

[root@nixos:/home/nixos]# mount /dev/nvme0n1p1 /mnt/actual-boot
[root@nixos:/home/nixos]# rsync -ah --progress /tmp/backed-up-boot /mnt/actual-boot

You can once again verify the contents of actual Boot with tree /mnt/actual-boot - compare the number of files as shown in the last line of the output.

Unomunt Boot partition with umount /mnt/actual-boot.

Now, you'll have to mount NixOS partition to edit hardware-configuration.nix.

Create a new directory mkdir /mnt/nixos-root. As mentioned, you may use any directory for mount - even /mnt/actual-boot, or even one with files already! But for conventional purposes we have crated /mnt/nixos-root.

Mount NixOS partition with mount /dev/nvme0n1p5 /mnt/nixos-root

Open up /mnt/nixos-root/etc/nixos/hardware-configuration.nix in your editor of choice (if you were booted to NixOS, the path would have been /etc/nixos/hardware-configuration.nix).

Locate a similarly-looking piece of code:

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/3C5B-FCD0";
      fsType = "vfat";
    };

This is the relevant snippet that tells NixOS where to search for Boot partition. uuid stands for "Universally Unique Identifier" - and, as name may suggest, it uniquely identifies a partiton (a disk) from the rest. Since we're using the Systemd Bootloader (the default one in NixOS), it stores versions of Linux kernel that NixOS boots with on the Boot partition, and not on NixOS partition. The development of Linux never stops, thus the kernels are being updated frequently, and when you perform an upgrade to your NixOS system, you'll very likely to land on a newer kernel as well. Taking into account that Initrd and 2 Linux kernels have a combined size nearing 100 MiB at the time of writing, it is no surprise 100 MiB is too small for a Boot partition! And that's why we had to resize the Boot partition in the first place. If Systemd tries to store more than 2 kernels, e.g. you have performed NixOS upgrades 3 times pulling a different kernel versions each time, 3rd definitely won't fit, and it will break either Windows or NixOS boot process, with the latter being more likely.

Finale

After so much ado - and we're finally done! You can now safely reboot into either Windows or NixOS. Good luck!

I will really appreciate, if you subscribe to my newsletter.
You inspire me to keep writing. Every reader counts.
You will receive email with link to confirm the subscription.