grub-reboot / efibootmgr --bootnext does not work for one-shot Windows boot in Ubuntu dual-boot — always returns to Ubuntu
https://askubuntu.com/questions/1567693/grub-reboot-efibootmgr-bootnext-does-not-work-for-one-shot-windows-boot-in-u
Title:
grub-reboot / efibootmgr --bootnext does not work for one-shot Windows boot in Ubuntu 26.04 dual-boot — always returns to Ubuntu
Tags: grub2, dual-boot, windows-11, uefi, efibootmgr
Body (question):
I have Ubuntu 26.04 + Windows 11 dual-boot (two NVMe drives, AMI UEFI v4.10, GRUB 2.14, Secure Boot off). I have a small script that is supposed to reboot the machine once into Windows and then come back to Ubuntu on the next power-on:
sudo efibootmgr --bootnext 0000 && sudo grub-reboot 1 && sudo reboot
No matter what I try — grub-reboot 'Windows 11', grub-reboot windows, efibootmgr --bootnext, or all of the above combined — after reboot I end up in Ubuntu again. Occasionally the machine starts looping in Windows instead. What are the possible causes and fixes?
ANSWER:
There are four independent failure modes that can combine to cause this. I debugged all of them. Here is the full breakdown.
Failure mode 1: grub-reboot by title silently fails if the menuentry has no --id
grub-reboot writes next_entry=<your argument> to /boot/grub/grubenv. At boot, GRUB matches this against the --id attribute of each menuentry — not against the display title.
If your /etc/grub.d/40_custom looks like:
menuentry 'Windows 11' {
...
}
then grub-reboot 'Windows 11' writes next_entry=Windows 11. GRUB finds no entry with --id "Windows 11", falls back to entry 0 (Ubuntu), and clears next_entry so the failure is invisible on the next look.
Fix: add --id windows to your custom menuentry:
menuentry 'Windows 11' --id windows {
insmod chain
search --no-floppy --fs-uuid --set=root 779F-7BE1
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
Then use sudo grub-reboot windows. Also set GRUB_DISABLE_OS_PROBER=true in /etc/default/grub — if os-prober generates a second Windows 11 entry without --id, it breaks the match even when your custom entry is correct.
Failure mode 2: bootmgfw.efi bounces back because of a stale hiberfil.sys
After fixing the --id issue, next_entry was consumed by GRUB (variable cleared, so GRUB did chain-load bootmgfw.efi), but the machine still returned to Ubuntu.
Evidence: the BCD file on the Windows EFI partition had a newer mtime than the last Ubuntu boot → bootmgfw.efi ran, touched BCD, then bounced back to Ubuntu ~90 seconds later.
Root cause: a 26 GB hiberfil.sys (Fast Startup / hibernate image) on the Windows partition. bootmgfw.efi tried to resume the hibernated session, failed, and returned control to firmware → BootOrder[0] = Ubuntu.
Fix:
# Mounts Windows C: and removes hiberfil.sys
sudo mount -o remove_hiberfile /dev/nvme0n1p4 /mnt
sudo umount /mnt
Long-term: in Windows run powercfg /h off to disable Fast Startup.
Diagnostic: If BootCurrent after an unsuccessful boot attempt is 0000 (Windows Boot Manager), BootNext worked but Windows itself bounced. If it's 0003 (Ubuntu shim), BootNext was ignored — see failure mode 3.
Failure mode 3: AMI firmware ignores BootNext
On some AMI boards efibootmgr --bootnext 0000 writes the EFI variable correctly but firmware boots BootOrder[0] anyway. After rebooting, BootNext is gone (consumed) but BootCurrent is still 0003 (Ubuntu) — the firmware silently discarded it.
Check:
# Right after rebooting back into Ubuntu:
sudo efibootmgr | grep -E 'BootCurrent|BootNext'
BootCurrent: 0003 with no BootNext: = BootNext was consumed but not honoured. Try disabling Fast Boot in the BIOS setup. On our machine this resolved after sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi (exact trigger unknown).
Failure mode 4 (the subtle one): arming BootNext AND grub-reboot simultaneously causes an infinite Windows loop
If you arm both as belt-and-suspenders:
sudo efibootmgr --bootnext 0000 # firmware one-shot
sudo grub-reboot 1 # GRUB one-shot
sudo reboot
…when BootNext is honoured, firmware boots bootmgfw.efi directly, bypassing GRUB. GRUB never runs → next_entry=1 stays in grubenv forever.
On the next boot (and every subsequent boot), GRUB reads next_entry=1 → boots Windows → Windows shuts down → GRUB reads next_entry=1 again → Windows → loop.
This symptom ("machine keeps booting into Windows") looks totally unrelated to the original script invocation, which succeeded. Very confusing.
The working solution
Two mutually exclusive paths — only one fires per script invocation:
#!/usr/bin/env bash
set -euo pipefail
GRUBENV=/boot/grub/grubenv
GRUB_ENTRY=1
WIN_PARTGUID=c186e69d-1471-423c-9b03-877f89268d4c # PARTUUID of Windows ESP
# PATH A: firmware BootNext — do NOT also set next_entry
win_bootnum=$(sudo efibootmgr -v \
| grep -i 'windows boot manager' \
| grep -i "$WIN_PARTGUID" \
| sed -n 's/^Boot\([0-9A-Fa-f]\{4\}\).*/\1/p' \
| head -1)
if [ -n "${win_bootnum:-}" ]; then
sudo efibootmgr --bootnext "$win_bootnum" >/dev/null
if sudo efibootmgr | grep -qi "^BootNext:[[:space:]]*${win_bootnum}$"; then
echo "BootNext armed → Boot${win_bootnum}"
sudo reboot
exit 0
fi
echo "WARNING: BootNext not confirmed, falling back to GRUB" >&2
fi
# PATH B: GRUB fallback (only when BootNext unavailable/unconfirmed)
sudo grub-editenv "$GRUBENV" unset initrdfail 2>/dev/null || true
sudo grub-editenv "$GRUBENV" unset prev_entry 2>/dev/null || true
sudo grub-reboot "$GRUB_ENTRY"
echo "GRUB next_entry=${GRUB_ENTRY} armed"
sudo reboot
Plus a boot-guard systemd service that clears stale next_entry on every Ubuntu startup (in case BootNext bypassed GRUB last time) and restores BootOrder if Windows reset it:
# /usr/local/sbin/ubuntu-boot-guard.sh
#!/bin/bash
set -euo pipefail
grub-editenv /boot/grub/grubenv unset next_entry 2>/dev/null || true
current_order=$(efibootmgr 2>/dev/null | awk '/^BootOrder:/{print $2}')
if [ "${current_order%%,*}" != "0003" ]; then
logger -t ubuntu-boot-guard "BootOrder was '$current_order', restoring"
efibootmgr --bootorder 0003,0000,0002 >/dev/null
fi
# /etc/systemd/system/ubuntu-boot-guard.service
[Unit]
Description=Clear stale GRUB next_entry and restore Ubuntu BootOrder
After=local-fs.target
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ubuntu-boot-guard.sh
RemainAfterExit=yes
[Install]
WantedBy=sysinit.target
sudo systemctl enable --now ubuntu-boot-guard.service
Quick diagnostic checklist
| Symptom |
Cause |
Fix |
next_entry is empty after failed boot |
GRUB consumed it but Windows bounced |
Check hiberfil.sys; check BootCurrent |
next_entry still set after reboot |
GRUB was bypassed (BootNext worked); ghost entry |
Add boot-guard service |
BootCurrent: 0003 after attempt |
BootNext ignored by firmware |
Disable Fast Boot in BIOS; try grub-install |
BootCurrent: 0000 after attempt |
BootNext worked but Windows bounced |
Delete hiberfil.sys; run powercfg /h off in Windows |
| Machine keeps booting Windows forever |
Ghost next_entry from dual-arming |
sudo grub-editenv /boot/grub/grubenv unset next_entry |