GRUB (Grand Unified Bootloader) is the default boot loader for most Linux distributions, including Arch Linux. While GRUB’s os-prober can detect other operating systems automatically, it does not always work – especially with custom Linux installations, Windows on separate drives, or ISO files you want to boot directly. Adding custom GRUB menu entries gives you full control over your boot process for dual-boot setups, recovery environments, and ISO booting.
This guide walks through adding custom menu entries to GRUB on Arch Linux. We cover entries for Windows, other Linux distributions, ISO images, and rescue/recovery modes. Every example uses the /etc/grub.d/40_custom file, which is the correct place for user-defined entries that survive GRUB updates. For full reference, see the Arch Wiki GRUB page.
Prerequisites
- A running Arch Linux system with GRUB already installed and working
- Root or sudo access
- Basic knowledge of your disk layout – partition UUIDs and filesystem types
- For dual-boot entries: the UUID of the target partition (obtained with
blkid) - For ISO boot entries: an ISO file stored on a local partition
Step 1: Understand GRUB Config Structure
GRUB’s configuration lives in /boot/grub/grub.cfg, but you should never edit that file directly. It gets regenerated every time you run grub-mkconfig. Instead, GRUB uses scripts in /etc/grub.d/ to build the final config. Here is what each file does:
00_header– Sets global defaults (timeout, default entry, colors)10_linux– Auto-detects Linux kernels on the current system20_linux_xen– Xen hypervisor entries30_os-prober– Auto-detects other operating systems (Windows, other Linux installs)40_custom– Your custom entries go here41_custom– Sources additional config from a separate file
List the scripts currently in /etc/grub.d/ to confirm the structure on your system:
ls -la /etc/grub.d/
You should see executable scripts numbered in order. GRUB processes them sequentially, so entries in 40_custom appear after the auto-detected entries:
total 80
drwxr-xr-x 2 root root 4096 Mar 22 10:00 .
drwxr-xr-x 4 root root 4096 Mar 22 10:00 ..
-rwxr-xr-x 1 root root 10627 Jan 15 12:30 00_header
-rwxr-xr-x 1 root root 18683 Jan 15 12:30 10_linux
-rwxr-xr-x 1 root root 14180 Jan 15 12:30 20_linux_xen
-rwxr-xr-x 1 root root 14400 Jan 15 12:30 30_os-prober
-rwxr-xr-x 1 root root 214 Jan 15 12:30 40_custom
-rwxr-xr-x 1 root root 215 Jan 15 12:30 41_custom
-rw-r--r-- 1 root root 483 Jan 15 12:30 README
The key takeaway: always add custom entries to 40_custom. This file persists across GRUB package updates, so your entries are safe.
Step 2: Add a Custom GRUB Menu Entry in /etc/grub.d/40_custom
Before adding any entry, you need the UUID of the target partition. Run blkid to list all partitions and their UUIDs:
sudo blkid
The output lists every block device with its UUID, filesystem type, and label. Identify the root partition of the OS you want to boot:
/dev/sda1: UUID="A1B2-C3D4" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI" PARTUUID="abcd1234-01"
/dev/sda2: UUID="f47ac10b-58cc-4372-a567-0e02b2c3d479" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="abcd1234-02"
/dev/sdb1: UUID="842a7abb-3547-4504-a9f8-bfbc479a3f0e" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="efgh5678-01"
In this example, /dev/sdb1 with UUID 842a7abb-3547-4504-a9f8-bfbc479a3f0e is the root partition of the second Linux installation. Write down the UUID – you need it for the menu entry. Now open the custom GRUB config file:
sudo vim /etc/grub.d/40_custom
Add a new menu entry below the existing comments. Here is an example for booting another Linux distribution (Gentoo in this case) from a separate partition:
#!/bin/sh
exec tail -n +3 $0
menuentry "Gentoo Linux" {
search --set=root --fs-uuid 842a7abb-3547-4504-a9f8-bfbc479a3f0e
linux /boot/vmlinuz-6.12-gentoo root=UUID=842a7abb-3547-4504-a9f8-bfbc479a3f0e rw quiet
initrd /boot/initramfs-6.12-gentoo.img
}
Replace the UUID with the actual UUID from your blkid output. The search --set=root --fs-uuid line tells GRUB which partition to use. The linux line specifies the kernel path on that partition, and initrd loads the initial ramdisk. Mount the target partition first to verify the exact kernel filename under its /boot directory.
Step 3: Add a Windows Boot Entry to GRUB
Windows uses its own bootloader (the Windows Boot Manager) stored on the EFI System Partition. You do not load a Linux kernel for Windows – instead, you chainload the Windows EFI bootloader. First, find the UUID of your EFI partition:
sudo blkid | grep -i vfat
Look for the partition with TYPE=”vfat” – that is typically the EFI System Partition. Note its UUID (the short format like A1B2-C3D4). Open the custom file:
sudo vim /etc/grub.d/40_custom
Add the Windows chainload entry. For UEFI systems (most modern hardware), use this format:
menuentry "Windows 11" {
insmod part_gpt
insmod fat
insmod chain
search --set=root --fs-uuid A1B2-C3D4
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
Replace A1B2-C3D4 with your actual EFI partition UUID. The insmod lines load the required GRUB modules for GPT partitions, FAT filesystem, and chainloading. For legacy BIOS systems (MBR partitioning), the entry looks different:
menuentry "Windows 11 (Legacy BIOS)" {
insmod part_msdos
insmod ntfs
insmod chain
search --set=root --fs-uuid ABCD1234EFGH5678
chainloader +1
}
On legacy BIOS, chainloader +1 loads the first sector of the Windows partition. Use the NTFS partition UUID (the long hex string from blkid) in this case.
Step 4: Add Another Linux Distro Entry
Adding a second Linux distribution follows the same pattern as Step 2. The key information you need is the UUID of that distro’s root partition and the kernel filename. Mount the other distro’s root partition to check:
sudo mkdir -p /mnt/other-linux
sudo mount /dev/sdb1 /mnt/other-linux
ls /mnt/other-linux/boot/vmlinuz*
This shows the available kernel images on that partition. Note the exact filename. Here is an example entry for Ubuntu installed on a separate partition:
menuentry "Ubuntu 24.04" {
search --set=root --fs-uuid d3f8a92c-1234-5678-abcd-ef0123456789
linux /boot/vmlinuz-6.8.0-45-generic root=UUID=d3f8a92c-1234-5678-abcd-ef0123456789 rw quiet splash
initrd /boot/initrd.img-6.8.0-45-generic
}
Each distribution names its kernel and initrd files differently. Ubuntu uses vmlinuz-VERSION-generic and initrd.img-VERSION-generic. Fedora uses vmlinuz-VERSION.fc42.x86_64. Always check the actual filenames. After verifying, unmount the partition:
sudo umount /mnt/other-linux
If the other Linux distro uses a separate /boot partition, you need to search for that partition’s UUID instead and adjust the kernel path accordingly (the path becomes /vmlinuz-* since /boot is the root of that partition).
Step 5: Add an ISO Boot Entry
GRUB can boot ISO images directly from a local partition using the loopback command. This is useful for keeping live distributions or rescue tools available without burning them to USB. Store the ISO on a partition that GRUB can read (ext4, FAT32, or NTFS). For this example, the ISO is at /iso/archlinux-2026.03.01-x86_64.iso on the root partition.
sudo vim /etc/grub.d/40_custom
Add the following entry for booting an Arch Linux ISO. The loopback command mounts the ISO as a virtual device:
menuentry "Arch Linux ISO" {
set isofile="/iso/archlinux-2026.03.01-x86_64.iso"
search --set=root --fs-uuid f47ac10b-58cc-4372-a567-0e02b2c3d479
loopback loop $isofile
linux (loop)/arch/boot/x86_64/vmlinuz-linux img_dev=/dev/disk/by-uuid/f47ac10b-58cc-4372-a567-0e02b2c3d479 img_loop=$isofile earlymodules=loop
initrd (loop)/arch/boot/x86_64/initramfs-linux.img
}
The loopback loop $isofile line makes GRUB treat the ISO as a block device named (loop). The kernel parameters img_dev and img_loop tell the Arch initramfs where to find the ISO at boot time. Different distributions require different kernel parameters for ISO booting – check each distro’s documentation for the correct values.
Step 6: Add a Recovery/Rescue Entry
A recovery entry boots your Arch Linux kernel into a minimal environment, useful when something breaks. You can boot into single-user mode (init level 1) or drop directly to a root shell. This does not need a separate partition – it uses your existing Arch kernel with different boot parameters.
sudo vim /etc/grub.d/40_custom
Add these recovery entries. The first boots into single-user mode (systemd rescue.target), and the second drops to an emergency shell before any services start:
menuentry "Arch Linux - Rescue Mode" {
search --set=root --fs-uuid f47ac10b-58cc-4372-a567-0e02b2c3d479
linux /boot/vmlinuz-linux root=UUID=f47ac10b-58cc-4372-a567-0e02b2c3d479 rw systemd.unit=rescue.target
initrd /boot/initramfs-linux.img
}
menuentry "Arch Linux - Emergency Shell" {
search --set=root --fs-uuid f47ac10b-58cc-4372-a567-0e02b2c3d479
linux /boot/vmlinuz-linux root=UUID=f47ac10b-58cc-4372-a567-0e02b2c3d479 rw systemd.unit=emergency.target
initrd /boot/initramfs-linux.img
}
The difference between the two: rescue.target mounts filesystems and starts a minimal set of services, giving you a single-user root shell with your filesystems available. emergency.target does almost nothing – it mounts the root filesystem read-only and drops you into a shell immediately, which is what you need when systemd itself is broken. For more on booting Linux into emergency and recovery mode, see our dedicated guide.
Step 7: Regenerate grub.cfg with grub-mkconfig
After adding all your custom entries to /etc/grub.d/40_custom, regenerate the GRUB configuration file. This reads all scripts in /etc/grub.d/ and produces the final /boot/grub/grub.cfg:
sudo grub-mkconfig -o /boot/grub/grub.cfg
The output should confirm that your custom entries were found. Look for lines mentioning your custom menu entries:
Generating grub.cfg ...
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/initramfs-linux.img
Found fallback initrd image(s) in /boot: initramfs-linux-fallback.img
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding custom menu entries ...
done
The “Adding custom menu entries …” line confirms GRUB picked up your 40_custom entries. Verify the generated config includes your entries by checking the end of the file:
grep "menuentry" /boot/grub/grub.cfg
This lists all menu entries in the final config. Confirm each of your custom entries appears in the output before rebooting.
Step 8: Configure GRUB Defaults in /etc/default/grub
The /etc/default/grub file controls GRUB’s behavior – timeout duration, default boot entry, kernel parameters, and more. Open it to review and adjust settings:
sudo vim /etc/default/grub
Here are the most useful settings for a multi-boot system:
# Boot the last selected entry by default
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
# Show the menu for 10 seconds before auto-booting
GRUB_TIMEOUT=10
# Show the menu (not hidden)
GRUB_TIMEOUT_STYLE=menu
# Default kernel parameters for Linux entries
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"
GRUB_CMDLINE_LINUX=""
# Enable os-prober to auto-detect other operating systems
GRUB_DISABLE_OS_PROBER=false
The key settings explained:
GRUB_DEFAULT=savedwithGRUB_SAVEDEFAULT=true– GRUB remembers your last selection and boots it next time. Without this, it always boots the first entryGRUB_TIMEOUT=10– gives you 10 seconds to select an entry. Set to 0 for instant boot (no menu) or -1 to wait indefinitelyGRUB_TIMEOUT_STYLE=menu– always shows the boot menu. Usehiddento hide it (press Shift to reveal) orcountdownto show a timerGRUB_DISABLE_OS_PROBER=false– enables automatic detection of Windows and other operating systems. On Arch Linux, this is disabled by default since GRUB 2.06
After making changes, regenerate the config again:
sudo grub-mkconfig -o /boot/grub/grub.cfg
If you want to set a specific default entry by name instead of using saved, use the exact menu entry title. For example, GRUB_DEFAULT="Windows 11" always boots Windows first. You can also use grub-set-default to set the default from the command line without editing the file. For additional GRUB security, consider protecting GRUB with a password.
Step 9: Troubleshoot Common GRUB Issues
When custom entries do not work, the problem is usually a wrong UUID, incorrect kernel path, or missing GRUB modules. Here are the most common issues and fixes.
Entry does not appear in boot menu
If your custom entry is missing from the boot menu after running grub-mkconfig, check that the 40_custom file is executable:
ls -la /etc/grub.d/40_custom
The file must have execute permission. If it does not, fix it:
sudo chmod +x /etc/grub.d/40_custom
Then regenerate the config with sudo grub-mkconfig -o /boot/grub/grub.cfg and check again.
Error: “no such device” at boot
This means GRUB cannot find the partition with the specified UUID. Double-check the UUID by running blkid again. A common mistake is copying the PARTUUID instead of the UUID – GRUB’s search --fs-uuid needs the filesystem UUID, not the partition table UUID.
Error: “file not found” for kernel or initrd
The kernel or initrd path in your entry does not match the actual filename on disk. Mount the target partition and verify:
sudo mount /dev/sdb1 /mnt/other-linux
ls -la /mnt/other-linux/boot/
Kernel version numbers change with updates. After a kernel update on the target OS, you need to update the filename in your custom GRUB entry and regenerate the config.
Windows entry boots to a black screen
This usually means the chainloader path is wrong. Verify the Windows EFI bootloader exists on the EFI partition:
sudo mount /dev/sda1 /mnt/efi
ls /mnt/efi/EFI/Microsoft/Boot/bootmgfw.efi
If the file exists, the path is correct. If not, Windows may be installed in legacy BIOS mode, which requires a different chainloader command (see Step 3). Also confirm you are using the correct EFI partition UUID – systems with multiple drives may have more than one EFI partition.
Reinstall GRUB after it breaks
If GRUB itself is broken and you cannot boot at all, boot from an Arch Linux installation USB, mount your root and EFI partitions, chroot in, and reinstall GRUB:
mount /dev/sda2 /mnt
mount /dev/sda1 /mnt/boot/efi
arch-chroot /mnt
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB
grub-mkconfig -o /boot/grub/grub.cfg
This reinstalls the GRUB EFI binary and regenerates the configuration, including all your custom entries from 40_custom. For creating bootable USB drives on Linux, several tools are available.
Conclusion
Custom GRUB entries in /etc/grub.d/40_custom give you precise control over multi-boot configurations on Arch Linux. Whether you are chainloading Windows, booting into another Linux distribution, loading ISO images directly, or setting up recovery entries – the process always follows the same pattern: identify the partition UUID, write the menu entry, and regenerate with grub-mkconfig.
For production systems, keep a recovery entry in your GRUB menu at all times. It takes seconds to add and can save hours when a bad kernel update or misconfigured service prevents normal boot. Also consider the official GRUB manual as reference for advanced features like theming, authentication, and scripting within GRUB entries.