Dual Booting NixOS and Windows: Difference between revisions

From NixOS Wiki
imported>Whitehouse3001
m Add a link to an article on dual-booting NixOS and Windows
Add method to boot Windows if it is installed on a different drive than NixOS
 
(20 intermediate revisions by 12 users not shown)
Line 1: Line 1:
This article explains various methods to have the bootloader prompt whether to boot windows or NixOS.
This section explains various methods to have the bootloader prompt whether to boot windows or NixOS.


== Autodetection with os-prober ==
== Autodetection ==
 
=== systemd-boot ===
 
When {{ic|systemd-boot}} is installed to the same EFI System Partition (ESP) that Windows uses, it will automatically detect the Windows installation ({{ic|/EFI/Microsoft/Boot/bootmgfw.efi}}) and present it as a boot option.
 
You can verify detected boot loaders by running the {{ic|bootctl}} command.
 
A system pre-installed with Windows might have a small ESP partition size that is not sufficient to store the kernel and initrd files for multiple NixOS generations. One solution is to create an additional [https://uapi-group.org/specifications/specs/boot_loader_specification/#the-partitions XBOOTLDR] partition and configure {{ic|systemd-boot}} to use it:
 
{{File|/etc/nixos/configuration.nix|nix|<nowiki>
{
  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/57D4-A2B2";
      fsType = "vfat";
    };
  fileSystems."/efi" =
    { device = "/dev/disk/by-uuid/3280-5418";
      fsType = "vfat";
    };
 
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
 
  boot.loader.efi.efiSysMountPoint = "/efi";
  boot.loader.systemd-boot.xbootldrMountPoint = "/boot";
}
</nowiki>}}
 
=== os-prober ===


<code>os-prober</code> is a tool to autodetect which other systems are present on the machine. Grub can be
<code>os-prober</code> is a tool to autodetect which other systems are present on the machine. Grub can be
Line 10: Line 39:
   # ...
   # ...
   boot.loader.grub.enable = true;
   boot.loader.grub.enable = true;
  boot.loader.grub.version = 2;
   boot.loader.grub.device = "nodev";
   boot.loader.grub.device = "/dev/sda";
   boot.loader.grub.useOSProber = true;
   boot.loader.grub.useOSProber = true;
   # ...
   # ...
Line 17: Line 45:
}</nowiki>}}
}</nowiki>}}


== Manual bootloader configuration ==
== Manual configuration ==
In case <code>os-prober</code> does not detect your windows partition you can configure your bootloader manually to find it.
In case <code>os-prober</code> does not detect your windows partition you can configure your bootloader manually to find it.


Line 24: Line 52:
All MBR bootloaders will need at least some configuration to chainload Windows.
All MBR bootloaders will need at least some configuration to chainload Windows.


==== Grub ====
==== Grub ====


Here is an example config:
Here is an example config:
Line 32: Line 60:
   # ...
   # ...
   boot.loader.grub.enable = true;
   boot.loader.grub.enable = true;
  boot.loader.grub.version = 2;
   boot.loader.grub.device = "/dev/sda";
   boot.loader.grub.device = "/dev/sda";
   boot.loader.grub.extraEntries = ''
   boot.loader.grub.extraEntries = ''
Line 42: Line 69:
Source: https://www.reddit.com/r/NixOS/comments/31lx3i/windows_and_nixos_dual_boot/
Source: https://www.reddit.com/r/NixOS/comments/31lx3i/windows_and_nixos_dual_boot/


=== EFI ===
=== UEFI ===


After setting up a 256mb EFI Partition dualboot should work out of the box (at least for windows10)
After setting up a 256mb EFI Partition dualboot should work out of the box (at least for windows10)


Source: https://zimbatm.com/journal/2016/09/09/nixos-window-dual-boot/
Source: [https://web.archive.org/web/20170523065625/https://zimbatm.com/journal/2016/09/09/nixos-window-dual-boot/ zimbatm.com/journal/2016/09/09/nixos-window-dual-boot]


Here is another article that documents dual booting NixOS and Windows on a Lenovo ThinkPad X1 Carbon (6th Gen): https://github.com/andywhite37/nixos/blob/master/DUAL_BOOT_WINDOWS_GUIDE.md
Here is another article that documents dual booting NixOS and Windows on a Lenovo ThinkPad X1 Carbon (6th Gen): https://github.com/andywhite37/nixos/blob/master/DUAL_BOOT_WINDOWS_GUIDE.md


==== Grub ====
==== Grub ====
<nowiki>systemd-boot</nowiki> can not load EFI binaries from other partitions,
and a pre-exisitng EFI partition from a Windows install may be smaller than we would like our <nowiki>/boot</nowiki> partition to be. If we still want Windows and NixOS to use the same EFI partition, we can use GRUB instead.


Here we assume:
Here we assume:
Line 86: Line 110:
           insmod search_fs_uuid
           insmod search_fs_uuid
           insmod chain
           insmod chain
           search --fs--uid --set=root $FS_UUID
           search --fs-uuid --set=root $FS_UUID
           chainloader /EFI/Microsoft/Boot/bootmgfw.efi
           chainloader /EFI/Microsoft/Boot/bootmgfw.efi
         }
         }
Line 95: Line 119:
}</nowiki>}}
}</nowiki>}}


Sources:
==== EFI with multiple disks ====
* [https://wiki.archlinux.org/index.php/GRUB#Windows_installed_in_UEFI-GPT_Mode_menu_entry Arch Wiki GRUB article]
 
===== systemd-boot =====
As systemd-boot cannot directly load binaries from other ESPs<ref>https://github.com/systemd/systemd/issues/3252</ref>, let alone other disks, we have to employ [https://search.nixos.org/packages?channel=unstable&show=edk2-uefi-shell&from=0&size=50&sort=relevance&type=packages&query=edk2-uefi-shell edk2-uefi-shell] to implement a chainloading strategy<ref>https://wiki.archlinux.org/title/Systemd-boot#Boot_from_another_disk</ref>. The basic config looks like this:{{File|/etc/nixos/configuration.nix|nix|<nowiki>
{ config, ... }:
 
{
  boot.loader = {
    systemd-boot.enable = true;
    efi.canTouchEfiVariables = true;
 
    # Copy EDK2 Shell to boot partition
    systemd-boot.extraFiles."efi/shell.efi" = "${pkgs.edk2-uefi-shell}/shell.efi";
    systemd-boot.extraEntries = {
      # Chainload Windows bootloader via EDK2 Shell
      "windows.conf" =
        let
          # To determine the name of the windows boot drive, boot into edk2 first, then run
          # `map -c` to get drive aliases, and try out running `FS1:`, then `ls EFI` to check
          # which alias corresponds to which EFI partition.
          boot-drive = "FS1";
        in
        ''
          title Windows Bootloader
          efi /efi/shell.efi
          options -nointerrupt -nomap -noversion ${boot-drive}:EFI\Microsoft\Boot\Bootmgfw.efi
          sort-key y_windows
        '';
      # Make EDK2 Shell available as a boot option
      "edk2-uefi-shell.conf" = ''
        title EDK2 UEFI Shell
        efi /efi/shell.efi
        sort-key z_edk2
      '';
    };
  };
}</nowiki>}}You can try if this works without changes, but most likely you have to modify the value of <code>boot-drive</code> first to match your hardware configuration.
 
First, make sure you <code>nixos-rebuild switch</code> or <code>nixos-rebuild boot</code> , then reboot your machine and select the entry "EDK2 UEFI Shell" from the systemd-boot menu. In this shell, run the command <code>map -c</code> to get a list of all "consistent" device mappings:<syntaxhighlight lang="text">
Press ESC in 1 seconds to skip startup.nsh or any other key to continue.
Shell> map -c
Mapping table
    HD0c3: Alias(s):FS0:;BLK7:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(3,GPT,5CBAF773-8FFA-11EB-952D-FCAA14203853,0x1DAA6000,0x32000)
    HD0d1: Alias(s):FS1:;BLK10:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,GPT,7F623BEA-5891-49EE-9980-6534716F0F50,0x800,0x1F4000)
</syntaxhighlight>Then, change to each of these drives by entering the drive name (not one of the aliases!) and check whether the Windows bootloader is present:<syntaxhighlight lang="text">
Shell> HD0d1:
HD0d1:\> ls EFI
Directory of: HD0d1:\EFI\
09/21/2024  22:05 <DIR>        4,096  .
09/21/2024  22:05 <DIR>            0  ..
09/21/2024  23:08 <DIR>        4,096  BOOT
09/21/2024  22:05 <DIR>        4,096  Linux
09/24/2024  08:30 <DIR>        4,096  nixos
01/01/1980  00:00          1,060,672  shell.efi
09/21/2024  23:08 <DIR>        4,096  systemd
          1 File(s)  1,060,672 bytes
          6 Dir(s)
HD0d1:\> HD0c3:
HD0c3:\> ls EFI
Directory of: HD0c3:\EFI\
03/28/2021  21:28 <DIR>        1,024  .
03/28/2021  21:28 <DIR>            0  ..
03/28/2021  21:28 <DIR>        1,024  Boot
03/28/2021  21:28 <DIR>        1,024  Microsoft
          0 File(s)          0 bytes
          4 Dir(s)
</syntaxhighlight>In this case, HD0d1 is the ESP of our NixOS installation, and HD0c3 is the ESP of Windows.
 
After entering the Windows ESP, you can boot into it by running <code>EFI\Microsoft\Boot\Bootmgfw.efi</code>. This is also useful if you have multiple Windows installations and want to find out which ESP belongs to which installation.
 
After this, you can change the value of <code>boot-drive</code> in the configuration snippet above, <code>nixos-rebuild switch</code> and reboot to boot into windows. Make sure that you use the actual device name, not one of the aliases, as these might not be available immediately on boot, making the shell invocation fail.
 
===== Grub =====
In Grub, the following might work:
 
{{File|/etc/nixos/configuration.nix|nix|<nowiki>
{ config, ... }:
 
{
  boot.loader = {
    efi.canTouchEfiVariables = true;
    grub = {
      enable = true;
      devices = [ "nodev" ];
      efiSupport = true;
      useOSProber = true;
    };
  };
}</nowiki>}}
 
== System time ==
 
System clock might be incorrect after booting Windows and going back to the NixOS. It can be fixed by either setting RTC time standard to UTC on Windows, or setting it to localtime on NixOS.
 
Setting RTC time standard to localtime, compatible with Windows in its default configuration:
 
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  time.hardwareClockInLocalTime = true;
}</nowiki>}}
 
See [https://wiki.archlinux.org/title/System_time#Time_standard Arch Linux wiki#System time].
 
== See also ==
* [https://wiki.archlinux.org/index.php/GRUB#Windows_installed_in_UEFI-GPT_Mode_menu_entry Arch Linux wiki#GRUB]
* [https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/boot/loader/grub/install-grub.pl NixOS GRUB installer] (check the code block beginning with <nowiki># install EFI GRUB</nowiki>)
* [https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/boot/loader/grub/install-grub.pl NixOS GRUB installer] (check the code block beginning with <nowiki># install EFI GRUB</nowiki>)
[[Category:Cookbook]][[Category:NixOS]]

Latest revision as of 10:06, 24 September 2024

This section explains various methods to have the bootloader prompt whether to boot windows or NixOS.

Autodetection

systemd-boot

When systemd-boot is installed to the same EFI System Partition (ESP) that Windows uses, it will automatically detect the Windows installation (/EFI/Microsoft/Boot/bootmgfw.efi) and present it as a boot option.

You can verify detected boot loaders by running the bootctl command.

A system pre-installed with Windows might have a small ESP partition size that is not sufficient to store the kernel and initrd files for multiple NixOS generations. One solution is to create an additional XBOOTLDR partition and configure systemd-boot to use it:

/etc/nixos/configuration.nix
{
  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/57D4-A2B2";
      fsType = "vfat";
    };
  fileSystems."/efi" =
    { device = "/dev/disk/by-uuid/3280-5418";
      fsType = "vfat";
    };

  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  boot.loader.efi.efiSysMountPoint = "/efi";
  boot.loader.systemd-boot.xbootldrMountPoint = "/boot";
}

os-prober

os-prober is a tool to autodetect which other systems are present on the machine. Grub can be told to use os-prober to add a menu-entry for each of them.

/etc/nixos/configuration.nix
{ config, pkgs, ... }: {
  # ...
  boot.loader.grub.enable = true;
  boot.loader.grub.device = "nodev";
  boot.loader.grub.useOSProber = true;
  # ...

}

Manual configuration

In case os-prober does not detect your windows partition you can configure your bootloader manually to find it.

MBR

All MBR bootloaders will need at least some configuration to chainload Windows.

Grub

Here is an example config:

/etc/nixos/configuration.nix
{ config, pkgs, ... }: {
  # ...
  boot.loader.grub.enable = true;
  boot.loader.grub.device = "/dev/sda";
  boot.loader.grub.extraEntries = ''
    menuentry "Windows 7" {
      chainloader (hd0,1)+1
    }
  '';
}

Source: https://www.reddit.com/r/NixOS/comments/31lx3i/windows_and_nixos_dual_boot/

UEFI

After setting up a 256mb EFI Partition dualboot should work out of the box (at least for windows10)

Source: zimbatm.com/journal/2016/09/09/nixos-window-dual-boot

Here is another article that documents dual booting NixOS and Windows on a Lenovo ThinkPad X1 Carbon (6th Gen): https://github.com/andywhite37/nixos/blob/master/DUAL_BOOT_WINDOWS_GUIDE.md

Grub

Here we assume:

  • the EFI partition has been mounted on /boot/efi
  • $FS_UUID is the UUID of the EFI partition
  • the boot.loader.systemd-boot.enable = true; line added to configuration.nix by nixos-generate-config has been removed
/etc/nixos/configuration.nix
{ config, ... }:

{
  boot.loader = {
    efi = {
      canTouchEfiVariables = true;
      # assuming /boot is the mount point of the  EFI partition in NixOS (as the installation section recommends).
      efiSysMountPoint = "/boot";
    };
    grub = {
      # despite what the configuration.nix manpage seems to indicate,
      # as of release 17.09, setting device to "nodev" will still call
      # `grub-install` if efiSupport is true
      # (the devices list is not used by the EFI grub install,
      # but must be set to some value in order to pass an assert in grub.nix)
      devices = [ "nodev" ];
      efiSupport = true;
      enable = true;
      # set $FS_UUID to the UUID of the EFI partition
      extraEntries = ''
        menuentry "Windows" {
          insmod part_gpt
          insmod fat
          insmod search_fs_uuid
          insmod chain
          search --fs-uuid --set=root $FS_UUID
          chainloader /EFI/Microsoft/Boot/bootmgfw.efi
        }
      '';
      version = 2;
    };
  };
}

EFI with multiple disks

systemd-boot

As systemd-boot cannot directly load binaries from other ESPs[1], let alone other disks, we have to employ edk2-uefi-shell to implement a chainloading strategy[2]. The basic config looks like this:

/etc/nixos/configuration.nix
{ config, ... }:

{
  boot.loader = {
    systemd-boot.enable = true;
    efi.canTouchEfiVariables = true;

    # Copy EDK2 Shell to boot partition
    systemd-boot.extraFiles."efi/shell.efi" = "${pkgs.edk2-uefi-shell}/shell.efi";
    systemd-boot.extraEntries = {
      # Chainload Windows bootloader via EDK2 Shell
      "windows.conf" =
        let
          # To determine the name of the windows boot drive, boot into edk2 first, then run
          # `map -c` to get drive aliases, and try out running `FS1:`, then `ls EFI` to check
          # which alias corresponds to which EFI partition.
          boot-drive = "FS1";
        in
        ''
          title Windows Bootloader
          efi /efi/shell.efi
          options -nointerrupt -nomap -noversion ${boot-drive}:EFI\Microsoft\Boot\Bootmgfw.efi
          sort-key y_windows
        '';
      # Make EDK2 Shell available as a boot option
      "edk2-uefi-shell.conf" = ''
        title EDK2 UEFI Shell
        efi /efi/shell.efi
        sort-key z_edk2
      '';
    };
  };
 }

You can try if this works without changes, but most likely you have to modify the value of boot-drive first to match your hardware configuration. First, make sure you nixos-rebuild switch or nixos-rebuild boot , then reboot your machine and select the entry "EDK2 UEFI Shell" from the systemd-boot menu. In this shell, run the command map -c to get a list of all "consistent" device mappings:

Press ESC in 1 seconds to skip startup.nsh or any other key to continue.
Shell> map -c
Mapping table
    HD0c3: Alias(s):FS0:;BLK7:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(3,GPT,5CBAF773-8FFA-11EB-952D-FCAA14203853,0x1DAA6000,0x32000)
    HD0d1: Alias(s):FS1:;BLK10:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,GPT,7F623BEA-5891-49EE-9980-6534716F0F50,0x800,0x1F4000)

Then, change to each of these drives by entering the drive name (not one of the aliases!) and check whether the Windows bootloader is present:

Shell> HD0d1:
HD0d1:\> ls EFI
Directory of: HD0d1:\EFI\
09/21/2024  22:05 <DIR>         4,096  .
09/21/2024  22:05 <DIR>             0  ..
09/21/2024  23:08 <DIR>         4,096  BOOT
09/21/2024  22:05 <DIR>         4,096  Linux
09/24/2024  08:30 <DIR>         4,096  nixos
01/01/1980  00:00           1,060,672  shell.efi
09/21/2024  23:08 <DIR>         4,096  systemd
          1 File(s)   1,060,672 bytes
          6 Dir(s)
HD0d1:\> HD0c3:
HD0c3:\> ls EFI
Directory of: HD0c3:\EFI\
03/28/2021  21:28 <DIR>         1,024  .
03/28/2021  21:28 <DIR>             0  ..
03/28/2021  21:28 <DIR>         1,024  Boot
03/28/2021  21:28 <DIR>         1,024  Microsoft
          0 File(s)           0 bytes
          4 Dir(s)

In this case, HD0d1 is the ESP of our NixOS installation, and HD0c3 is the ESP of Windows.

After entering the Windows ESP, you can boot into it by running EFI\Microsoft\Boot\Bootmgfw.efi. This is also useful if you have multiple Windows installations and want to find out which ESP belongs to which installation.

After this, you can change the value of boot-drive in the configuration snippet above, nixos-rebuild switch and reboot to boot into windows. Make sure that you use the actual device name, not one of the aliases, as these might not be available immediately on boot, making the shell invocation fail.

Grub

In Grub, the following might work:

/etc/nixos/configuration.nix
{ config, ... }:

{
  boot.loader = {
    efi.canTouchEfiVariables = true;
    grub = {
      enable = true;
      devices = [ "nodev" ];
      efiSupport = true;
      useOSProber = true;
    };
  };
}

System time

System clock might be incorrect after booting Windows and going back to the NixOS. It can be fixed by either setting RTC time standard to UTC on Windows, or setting it to localtime on NixOS.

Setting RTC time standard to localtime, compatible with Windows in its default configuration:

/etc/nixos/configuration.nix
{
  time.hardwareClockInLocalTime = true;
}

See Arch Linux wiki#System time.

See also