How to use a USB key as an EFI switch
Or how I finally found a better solution than grub.
Introduction
I’ve been struggling to get into writing and coding these days. For a while I thought this was because I was doing so much of it at work (which I’m now excited to write about!) but it turns out I was just not enjoying my personal coding setup. I’ve been using Pop!_OS on an old Dell XPS 15 for 6 years now, and just starting it felt like a slog, let alone spending a whole day writing code. Everything was slow, buggy and just not pleasant. This is made much worse when comparing to the beast of Macbook (the Developer Pro edition) I have from work which makes it painfully obvious how much of a patchwork my other setup was. Then, my wife suggested I just use our gaming computer which I built for her around the same time I got the Dell. However that one runs Windows 11, and while I must admit Windows Subsystem Linux is a great step towards a better Windows developer experience, everytime I use it I end up frustrated by the constant limitations due to the virtualization layer.
So, I moved some of the games away from the second hard drive before carving a large section for a Linux dual-boot. I was thinking Fedora (I always go Ubuntu for the out-of-the-box compatibility, but I just love Fedora more) since I don’t have annoying laptop WiFi or integrated GPU issues, and since I was curious I decided to try the atomic distribution Fedora Silverblue. I had never really thought about it, but the philosophy of keeping the core system read-only and having only a mutable user home felt like something I always wanted but didn’t know was possible. After an hour or so, I was all set-up with my clean installation, and I even managed to add a systemd-bootloader so I can conveniently switch between Windows and Linux1. At that point I was pretty happy with myself and started setting everything up, until I reached the dreaded nvidia drivers mess. I’ll pass the details but I ended up trying to rebase onto a more interesting (for my use-case at least) distribution (still Silverblue-based) called Project Bluefin (it was the first time I discovered it but it looked really interesting and quite well-done out of the box), but somehow couldn’t install the gpu drivers properly due to some remnant kernel from the rollback-available Silverblue.
There I was, at 1:30AM, stuck in a situation which I’m sure someone more experienced would have solved immediately, but no amount of prompting ChatGPT or Googling around seemed to solve my problem. So I finally decided I better just erase the whole thing and start over with a clean install of Bluefin. This long story, while mostly just helping me vent about a less relaxing evening than I’d hoped, is also a motivation as to why, after all this, I didn’t feel like re-making my bootloader which I knew might not survive a system upgrade anyway. So at that point, I was finally coming to terms with the idea of just manually hammering on F11 at startup in hope that I could catch the surprisingly-short BIOS window. It didn’t take more than 3 restarts to realize this was going to be annoying: with an atomic distribution, reboots are essential to apply changes to the ostree. I could have just put Linux as the default boot option, but then booting on Windows would have been equally annoying.
That’s when I remembered my tinkering of the previous day with systemd-bootloader, and thought maybe I can use something like that, but externally. I grabbed an old 2Gb USB key and, with a lot of trial and error and GPT-5’s help, I managed to basically copy the bootloader from the Linux side onto it. Then in the BIOS, I set it up so that USB keys boot first, and I put Windows as the first of the hard drive EFI partitions. That way, when the USB key is plugged in, the PC automatically boots onto the Linux partition, otherwise Windows is loaded. I still don’t know if this is actually a common thing people do, but as someone who’s played around with dual boots for almost two decades, this actually feels like the perfect solution. I don’t have to worry about any reboot when I’m using the PC, and I can just unplug the USB key when I’m not so my wife isn’t bothered by it. I’m now writing this article mostly so I can remember next time how I did this, but I hope it’ll also help someone else.
How does it work?
Okay so the instructions are actually quite simple (once you figure out what is needed), although you might need to tweak things depending on your setup. First you should mount both your USB key (formatted as FAT32) and the EFI partition of your Linux installation. To find these, start by listing all the partitions of your drive
I put mine as an example here (with the two drives sda and sdb and the USB key sdc). In my case, the Linux EFI partition is /dev/sda1 and the USB partition is /dev/sdc1, so let’s mount them in temporary folders
Once this is done, you need to create the following structure in your usbboot partition
The folders you can create manually. The .efi files you should copy from /mnt/linux_esp/. If you play around with a different distribution, the exact structure might be different but the logic is the same: some of these are merely pointing the boot towards the others so whatever structure you have will probably do the same. The grub.cfg you need to manually create with the following content
NOTE
The UUID in the first line should be set from the output of the command lsblk -f and this should be the UUID of the /boot partition. This is different from the one we used to copy the files (which was /boot/efi)!
The loader files are also pretty simple and just setup the bootloader entries which will be invisible anyway (we’ll set a timeout of 0s):
And voilà! You can unmount both partitions using sudo umount /mnt/usbboot and similarly for /mnt/linux_esp and this should all work. I hope this will be as useful to you as it is to me.
Footnotes
-
This was actually more difficult than I had anticipated since both systems have a separate EFI partition, each on a separate drive. Eventually it was just simpler to copy the bootloader portion from windows into the linux systemd-boot portion and make an entry out of that. ↩