Booting multiple ISOs with GRUB2 from one single USB stick


Updated: 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/ or isolinux/ 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:
    1. A FAT32 partition. Debian's installer cannot read from ext3/4 partitions (this took me a while to find out...)
    2. An unpacked ISO (be it regular or the netinstaller).
    3. 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
     }