Booting multiple ISOs with GRUB2 from one single USB stick
Published: 2015-12-17Updated: 2016-01-08
Rationale
I recently had to clean up (and upgrade) a friend's system. It had been a while since I had to do that on location, and I had just this one crappy USB2 stick on me (and my laptop with some ISOs I keep around). I wrote a Linux Live ISO to my USB stick to back up his data (his system was ridden with malware), then I had to reflash that same USB stick with the Windows 10 ISO. Then the stick got corrupted at some point, so that meant: another new flash, and more time wasted... I do have multiple USB sticks of course - I just left them at home!
At that point, I realised I needed just one stick, but with a lot of ISOs - preferably the current Windows one, some Linux live ones, and some nice tools like the Universal Boot CD or the Network Security Toolkit. There are nice solutions around like Yumi, which (despite just listing Windows 7/8/8.1 for now) does support booting the Windows 10 installer, alongside several Linux ISOs. However, it relies on legacy GRUB, and with GRUB2 having improved loopback support (essential for using ISOs as-is), that meant I had to look further.
Initial setup & Windows installer
Yumi gave me some insight on how to build a basic multiboot USB stick. I ordered a 16 GB USB3 model, and got to work. You can format the whole stick as one big FAT32 partition (remember: hard 4 GiB file limit), or split the space between e.g. a FAT32 partition (at least 4 GiB free space) and Linux partition(s). I decided for myself it would be cleaner to keep separate partitions for some installers I update frequently, like Debian and OpenELEC.
Double-check your paths and block devices before you do this!
Install GRUB2 on the first partition. Usually, that goes like this:
$ sudo grub-install --root-directory=/media/usb /dev/sdb --target=i386-pc
After that, copy over your distribution's grub.cfg
so you have a starting point. Make sure to set your root and UUID correctly. If you want a nice background, you can copy it over to /boot/grub/
- apparently GRUBs picks up the first image in there. I set mine explicitly with the following line in grub.cfg:
set background_image -m stretch /boot/grub/lines-grub-1920x1080.png
Now on to the partitions. My layout looks like this:
$ sudo parted /dev/sdb print
Model: JetFlash Transcend 16GB (scsi)
Disk /dev/sdb: 15,6GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
Number Start End Size Type File system Flags
1 1049kB 4201MB 4200MB primary fat32 boot, lba
2 4201MB 15,6GB 11,4GB extended
5 4202MB 4632MB 430MB logical fat32
6 4633MB 5000MB 367MB logical ext4
7 5001MB 5053MB 52,4MB logical ext4
8 5053MB 15,6GB 10,5GB logical ext4
The first FAT32 partition holds GRUB2 and the Windows 10 installer files; the second the contents of Debian's netinstall ISO. The first ext4 partition holds the OpenELEC installer, with the second being a live partition for it. The remainder of the stick is claimed by one big ext4 partition (haven't disabled journaling so far) that holds syslinux and various Linux ISOs and installers. The extra separate partitions allow me to keep the layout more or less clean: I just wipe everything on the Debian partition if i want to replace its installer files (which happens more often than I'd update the Windows installer).
When it comes to adding ISOs, there are a few gotchas - I'll list them quickly so you can work around them if you start adding ISOs yourself:
- Your first stop is the ISO's bootloader itself. Mount the ISO as a loopback device, and check the
boot/
orisolinux/
directories for boot parameters. Your first hints as to what you need to feed GRUB2 will be in there. - The Windows installer does not work from the ISO; you need to unpack it into the root of your first partition. As far as I can tell, that means you're tied to the legacy MBR and MS DOS partition table, since UEFI/GPT setups require a bootloader partition (I have not tested if this partition needs to be the first one or not). Additionally, that first partition needs to be FAT32 or NTFS, otherwise the Windows installer will not be able to read off it.
- Similarly, some Linux ISOs do not respond well to being called directly by GRUB2. Like with Windows, mount these ISOs, and copy their contents to your USB stick. Make sure you copy over everything (e.g. Kodibuntu has a hidden
.disk
directory). For me, unpacking the ISO is a last resort - it makes updating installers more tedious and you cannot just verify the ISO's checksum anymore to make sure everything's still OK if your setup is behaving weirdly.
Linux installers and Live CD's
Some ISOs run well without too much fiddling; others need a lot of work. Your mileage may vary; some people have tried before, but a lot of stuff you'll find online applies to old ISOs, and as such boot parameters etc. may already be deprecated. A quick overview of the annoyances I bumped into:
- Debian's ISOs are known to be stubborn. You will need:
- A FAT32 partition. Debian's installer cannot read from ext3/4 partitions (this took me a while to find out...)
- An unpacked ISO (be it regular or the netinstaller).
- A ramdisk that supports 'HD media' to locate the ISO on (this also includes USB sticks). The regular ramdisk the ISOs come with does not support loading the ISO off hard drives. Debian's live images are said to work, but with broadband widely available over here and leaner and more up to date live ISOs around, there's really no reason for me to try it.
- Kodibuntu's ISO needs to be unpacked to the root of your partition (ext4 is no problem). You only need to copy over the
casper/
and.disk/
directories. - Manjaro 15.12 RC1, Xubuntu 15.10 and Linux Mint 17.3 worked with some minor tweaks to their boot parameters. For Manjaro, this forum post got me up and running quickly.
- For the Ultimate Boot CD, you need syslinux's
memdisk
utility. Grab the syslinux tarball, extract it and copy the memdisk binary to e.g./syslinux/
. For some background, read here. - OpenELEC is pretty easy: just two files - KERNEL and SYSTEM. You need to pass it the UUID of the partition it resides on.
- The Network Security Toolkit is built on top of a recent Fedora ISO. The Fedora forum, combined with this Sourceforge thread got me a booting ISO. You need to extract it for it to boot.
At this point, I have the following on my USB stick:
- AVG Rescue CD (ISO)
- Debian 8 netinstall (extracted)
- GParted Live (ISO)
- Hardware Detection Tool (ISO)
- Kodibuntu (extracted)
- Linux Mint 17.3 (ISO)
- Manjaro 15.12 RC1 XFCE (ISO)
- Memtest86+ (bootable binary)
- Network Security Toolkit (extracted)
- OpenELEC (extracted)
- System Rescue CD (ISO)
- Universal Boot CD (ISO)
- Windows 10 installer (extracted)
- Xubuntu 15.10 (ISO)
A clean GRUB menu & localisation
A lot of installers offer multiple boot options. For some, it might be interesting to have them in your boot menu, but that significantly clutters your list. Here's where GRUB2's submenu
feature comes in handy - it allows you to group boot entries. You can also assign hotkeys (a single letter, and some special keys) to quickly jump to often used entries or submenus. See the OpenELEC and Utilities GRUB2 entries below for examples. You'll see in the entries below some ISOs or installers are booted with specific keyboard layouts. Again, this might take a bit of fiddling and searching the internet for what works where.
GRUB2 Entries
You can find the GRUB2 entries below for each of those.
AVG Rescue CD
menuentry "AVG Rescue CD" --hotkey=a { insmod ext2 set root='hd0,msdos8' loopback loop /avg_arl_cdi_all_120_150814a10442.iso linux (loop)/isolinux/vmlinuz max_loop=255 vga=791 init=linuxrc -- initrd (loop)/isolinux/initrd.lzm }
Debian 8 netinstall
menuentry "Debian 8.2 Netinstall 64bit" --hotkey=d { insmod ext2 set root='hd0,msdos5' set isofile='debian-8.2.0-amd64-netinst.iso' loopback loop /$isofile linux (loop)/install.amd/gtk/vmlinuz boot=/ findiso=$isofile noprompt vga=788 noeject noprompt lang=fr_FR quiet --- initrd /initrd.gz }
GParted Live
menuentry "GParted Live (from RAM)" --hotkey=g { insmod ext2 set root='hd0,msdos8' set isofile='/gparted-live-0.24.0-2-amd64.iso' loopback loop $isofile linux (loop)/live/vmlinuz iso-scan/filename=$isofile boot=live union=overlay username=user config components quiet noswap findiso=$isofile toram=filesystem.squashfs vga=791 ip= net.ifnames=0 nosplash -- initrd (loop)/live/initrd.img }
Hardware Detection Tool
menuentry "Hardware Detection Tool" --hotkey=h { insmod ext2 set root='hd0,msdos8' linux16 /syslinux/memdisk iso initrd16 /hdt-0.5.2.iso }
Kodibuntu
submenu "Kodibuntu 14 64bit" --hotkey=k { menuentry "Live (Try without installing)" --hotkey=l { insmod ext2 set root='hd0,msdos8' linux /casper/vmlinuz boot=casper locale=fr_FR bootkbd=fr lang=fr_FR quiet splash -- initrd /casper/initrd.lz } menuentry "Install Kodibuntu" --hotkey=i { insmod ext2 set root='hd0,msdos8' linux /casper/vmlinuz file=/cdrom/preseed/ubuntu.seed boot=casper only-ubiquity debug-ubiquity locale=fr_FR bootkbd=fr console-setup/layoutcode=fr lang=fr_FR quiet splash -- initrd /casper/initrd.lz } menuentry "Check disk for defects" --hotkey=c { insmod ext2 set root='hd0,msdos8' linux /casper/vmlinuz boot=casper integrity-check locale=fr_FR bootkbd=fr lang=fr_FR quiet splash -- initrd /casper/initrd.lz } }
Linux Mint 17.3
menuentry "Linux Mint 17.3 Cinnamon 64bit" --hotkey=m { insmod ext2 set root='hd0,msdos8' loopback loop /linuxmint-17.3-cinnamon-64bit.iso linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/linuxmint-17.3-cinnamon-64bit.iso quiet lang=fr_FR noeject splash -- initrd (loop)/casper/initrd.lz }
Manjaro 15.12
menuentry "Manjaro 15.12 RC1 XFCE 64bit" { insmod ext2 set root='hd0,msdos8' set isofile='/manjaro-xfce-15.12-rc1-x86_64.iso' probe -u $root --set=partiuuid loopback loop $isofile linux (loop)/manjaro/boot/x86_64/manjaro img_dev=UUID=$partiuuid img_loop=$isofile misobasedir=manjaro misolabel=MJRO1512 nouveau.modeset=1 i915.modeset=1 radeon.modeset=1 logo.nologo lang=fr_FR quiet splash -- initrd (loop)/manjaro/boot/x86_64/manjaro.img (loop)/manjaro/boot/intel_ucode.img }
Memtest86+
menuentry "Memtest86+" --hotkey=m { insmod ext2 set root='hd0,msdos8' linux16 /memtest86+-5.01.bin }
Network Security Toolkit
menuentry "Network Security Toolkit 2" --hotkey=n { insmod ext2 set root='hd0,msdos8' linux /NST/isolinux/vmlinuz0 rootfstype=auto rd.live.image root=UUID=7f7cc688-d6af-40eb-b607-32a9eb041c2b liveimg live_dir=/NST/LiveOS/ rd.luks=0 rd.md=0 rd.dm=0 audit=0 systemd.unit=graphical.target nstrelocate=false lang=fr_FR locale=fr_FR bootkbd=fr ro quiet rhgb initrd /NST/isolinux/initrd0.img }
OpenELEC
submenu "OpenELEC" --hotkey=o { menuentry "OpenELEC 6.0 Installer" --hotkey=i { insmod ext2 set root='hd0,msdos6' linux /KERNEL boot=UUID=05fb0140-3d88-4f1f-829b-3036097aeb67 installer quiet tty vga=current } menuentry "OpenELEC 6.0 Live" --hotkey=l { insmod ext2 set root='hd0,msdos6' linux /KERNEL boot=UUID=05fb0140-3d88-4f1f-829b-3036097aeb67 quiet tty vga=current } }
System Rescue CD
menuentry "System Rescue CD" --hotkey=s { insmod ext2 set root='hd0,msdos8' loopback loop /systemrescuecd-x86-4.6.1.iso linux (loop)/isolinux/rescue64 setkmap=be isoloop=/systemrescuecd-x86-4.6.1.iso initrd (loop)/isolinux/initram.igz }
Universal Boot CD
menuentry "Ultimate Boot CD" --hotkey=u { insmod ext2 set root='hd0,msdos8' linux16 /syslinux/memdisk iso initrd16 /ubcd535.iso }
Windows 10 installer
menuentry "Windows 10 64bit" --hotkey=w { insmod ntfs set root='hd0,msdos1' ntldr /bootmgr boot }
Xubuntu 15.10
menuentry "Xubuntu 15.10 64bit" --hotkey=x { insmod ext2 set root='hd0,msdos8' set isofile='/xubuntu-15.10-desktop-amd64.iso' loopback loop $isofile linux (loop)/casper/vmlinuz.efi file=/preseed/xubuntu.seed boot=casper iso-scan/filename=$isofile lang=fr_FR noeject quiet splash -- initrd (loop)/casper/initrd.lz }