Reboot to Windows
My PC dual-boots Windows and Linux, but I use Linux ~95% of the time. Since I like my PC to boot as quickly as possible, I’ve settled on the following setup:
- The PC boots directly into Linux. There is no interactive boot menu.
- I’ve added Reboot to Windows options to my login manager and desktop environment.
- The PC reboots to Windows automatically after two minutes if I haven’t logged in.
If you’re curious how any of this works, read on!
Prerequisites
Before I dive into the details, please note that these solutions require that your machine:
- boots using UEFI,
- has separate EFI boot entries for Linux and Windows,
- doesn’t use a fancy boot menu that remembers your previous selection.
The first two criteria are easy to verify from Linux: run efibootmgr
, and check that there are
entries for both Linux and Windows.
$ efibootmgr BootCurrent: 0000 Timeout: 0 seconds BootOrder: 0000,0002,0001,0003,0004,0005,0006 Boot0000* Arch Linux HD(2,GPT,47be9262-66d7-7743-be45-99e62de[…] Boot0001* UEFI OS HD(1,GPT,dd9ddf9f-afcd-4776-9d72-db7cc81[…] Boot0002* Windows Boot Manager HD(2,GPT,7dd2e191-7af4-4540-a427[…] […]
You can see that, for my machine, 0000
is Linux and 0002
is Windows, and 0000
is the one that
will be booted first.
The third criterion is important because these solutions assume that the machine will boot directly into Linux by default. If your Linux installation came with an interactive boot menu (such as GRUB), you can likely reconfigure that boot menu to automatically default to Linux.
BootNext
UEFI supports overriding the boot order for the next boot only by setting the BootNext
EFI
variable. For example, if I want my machine to boot into Windows next, I can run:
$ sudo efibootmgr --bootnext 0002 BootNext: 0002 BootCurrent: 0000 […remaining output same as before]
reboot-to-windows script
In the above sections, we saw that my machine’s boot ID for Windows is 0002
. I didn’t want to
hard-code this ID (since it could change if I re-install in the future), so I wrote a script to find
it and reboot the machine.
#!/bin/bash
if output=$(efibootmgr | grep Windows) && [[ $output =~ Boot([[:xdigit:]]{4}) ]]; then
windows_id=${BASH_REMATCH[1]}
else
echo >&2 "No EFI boot entry found matching 'Windows'"
exit 1
fi
if ! sudo efibootmgr --bootnext "$windows_id"; then
echo >&2 "Failed to set next boot; aborting"
exit 1
fi
systemctl --quiet --no-block reboot
sudo configuration
efibootmgr
needs elevated privileges to modify EFI variables. To avoid being prompted
for a password, I added the following to /etc/sudoers.
%wheel ALL=(ALL:ALL) NOPASSWD: /usr/bin/efibootmgr ^--bootnext [[:xdigit:]]+$
This allows any member of the wheel group to run this specific command without providing a
password. In case you’re not familiar with configuring sudo, note that you should always use the visudo
command to do so.
Login manager configuration
I use greetd as my login manager, but this should work for any login manager that supports Wayland sessions.
[Desktop Entry]
Name=Reboot to Windows
Exec=/home/colin/bin/reboot-to-windows
Type=Application
Automatic reboot to Windows
I achieve this with a systemd service and timer.
[Service]
Type=oneshot
ExecCondition=bash -c "! loginctl list-sessions --json=short | jq -e '.[]|select(.seat != null && .user != \"greeter\")'"
ExecStart=/home/colin/bin/reboot-to-windows
The interesting part of this service is the ExecCondition
, which uses jq to parse the JSON from
loginctl
and look for any active sessions for users other than greeter (the user that my login
manager runs as). The -e
flag makes jq’s exit code reflect whether a session was found, and the
!
at the start of the pipeline negates the result so that the condition fails if any session was
found (meaning the reboot should not proceed).
The corresponding timer triggers this service to start two minutes after the machine boots.
[Timer]
OnActiveSec=2m
[Install]
WantedBy=multi-user.target
Remember to enable the timer with systemctl enable reboot-to-windows.timer
.