Journey towards a maintenance free system multi booting
Tags: grub, boot, menu, efi
Published: 2021-10-28 00:05:35

---
title: Journey towards a maintenance free system multi booting
series: maintenance free grub boot
published: true
description: To simply the situation for people that dual boot/multi boot their computer systems, and eventually becomes maintenance free booting.
tags: grub,boot,menu,EFI
cover_image: https://user-images.githubusercontent.com/422244/139099880-93d47280-beab-49c5-a0f1-4ed00e5a3a57.png
---

## Problem statement and goal

This article tries to simply the situation for people who dual boot/multi boot their computer systems, be them Debian, Mint, Arch, Fedora, openSUSE, etc, or Windows.

For multi booting all the operation systems, I have a standalone boot partition as well as an UEFI ESP partition. From there, any operation systems on my disk can be booted. A Debian boot entry looks like this:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub1.cfg %}

Let me stress again that this is just one single entry. There are also several _"Advanced options"_ that goes with each Linux system, so my multi-boot `grub.cfg` file looks scary, because it is humongous. But that's not the only problem.

Problem #1: Because it is in a standalone boot partition, I normally don't need to mount it, but whenever I got my kernel manually or automatically upgraded, I have to remember to _mount this partition and  update the boot entries_ there. Otherwise, 

- I will not be able to boot up my system again, if the old kernels are somehow/automatically purged.
- I will not be able to take advantage of the new kernel, even if they have been installed.

Problem #2: I like to **bring my `40_custom` up to the top** of my menu selection, instead of _down at the bottom_. 

Problem #3: I like to **order my boot entries** in the order I want, instead of being _determined by their partition orders_.

Although `grub` can generate boot entries automatically,  all above problem are blind spots of `grub`, and you have to come up with your own ways of dealing with them. 

The good news is that the `grub.cfg` file does not have to be so humongous, scary, and messy. At the end of this article, you'll find that things can be pretty simple. **Scroll to the bottom** to see if you don't want to go through the discovery journey that leads towards it. I.e. you'll find that 

Things can be so simple that people can start writing/maintaining the grub boot menu by hands now. And in the next blog, we can see that things can be even more simpler, with the help of [easygen](https://github.com/go-easygen/easygen/wiki).

Let the journey begin -- we'll going to go through a series of steps to simplify the `grub.cfg` file so that in the end, they can be edited manually. Now, step number one,

<bp />

## Remove the "Advanced options"

Let's take a look how big my `grub.cfg` file is:

    $ wc /boot/grub/grub.cfg
      355  1338 15006 /boot/grub/grub.cfg

Over 350 lines! Let's first simplify the `grub.cfg` file by remove the "Advanced options" menu entries, as I've been using Linux for over 20 years, and not a single chance had I ever need to dip into those options. Moreover, the grub menu can be easily edited if I want, say to boot into single mode. They can go for sure:

```sh
$ sed '/^submenu .Advanced options for /,/^}/d' /boot/grub/grub.cfg | wc
    183     581    5602
```

The file size almost dropped in half and now is under 200 lines. 

<bp />

## Remove the unnecessary if conditions

Look at the following lines in the above posted grub boot menu:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub2.cfg %}


Has anyone ever wondering what it is doing?

Well, [it turns out](https://askubuntu.com/a/1177035/843820) that

> ***the conditional code checking for this feature is pointless in all modern Linux distributions***

So why still keep it around?

These `if` conditions are second that need to go. After that, a single boot entry will look like:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub3.cfg %}

## Remove the unnecessary module loading

Now, why are there so many `insmod` statements in ***each*** boot entry? My computer contains either Linux or Windows, and they reside all in GPT, as ext4 partitions. I.e.,

All my Linux will need the same modules, so it doesn't make sense to repeat every one of them in ***each*** individual boot entry. 

Can I **load all the modules I need only once** at the beginning, and save each individual boot entries from _repeating themselves_?

It turns out that I can. So now my Linux boot entry looks like this:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub4.cfg %}

## A little help please

It'll be tedious to go through each boot entry and do the above clean up, so a little help from a script will definitely be the way to go, and [here](https://gist.github.com/suntong/cafb2fc8eaaeb77871927abae16c54f0#file-grub2clean-sed) it is:

{% gist https://gist.github.com/suntong/cafb2fc8eaaeb77871927abae16c54f0 file=grub2clean.sed %}


## Toward maintenance free booting

Now it is time to tackle Problem #1 -- whenever my kernel is upgraded, I don't want to 

- mount my boot partition, and
- update the boot entries there

I want my boot partition's boot entries to be able to survive such change and always boot to the latest kernel, whatever they are. Is that possible? Well,

It sure can, using the trick like this:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub5.cfg %}


## Maintenance free, at last!

Look at the `linux /vmlinuz` line, there is still a `root=UUID=xxx` parameter there. Now what if I reformat the partition and install a new Linux? The `UUID` will surely change and the above boot entry will surely not be able to survive such change.

This hurdle is the most difficult of all. Luckily someone has already looked into such problem, but sadly, nobody else even care, judging from the up-votes from the SO question and answer. Anyway, here, the [prestige](https://en.wikiquote.org/wiki/The_Prestige_(film)) of the whole article, here is the solution:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub6.cfg %}

The `probe -u $root --set=rootuuid` line will set the `$rootuuid` variable, then it get passed to the next `linux /vmlinuz` line. 

Hooray~~, problem solved!!


## One step further

Now, take a closer look at the `search --no-floppy --fs-uuid ...` line.  
What does it do? 

It actually set the `$root` grub variable according to a ***fixed*** `UUID` value on that line, and therefore change the boot partition, which will _undermine the whole effort_ of our above attempt. 

That will surely need to go away.


## Summary of the journey

The above discovery journey was a step by step one and it has been logged [here](https://gist.github.com/suntong/cafb2fc8eaaeb77871927abae16c54f0#file-os0_default-cfg):

{% gist https://gist.github.com/suntong/cafb2fc8eaaeb77871927abae16c54f0/586786bb50633b1c21805e371a315a4252be8b9e file=os0_default.cfg %}


The boot entries were build from the ground up and move step by step to the top. All entries are verified to be working fine.

<a name="conclusion"/>

## Conclusion

Once upon a time, people write their own boot menu. But `grub2` came alone and people just gave up such practice and rely solely on the machine.

The journey in this article show that things do not have to be that way as the only way -- we can still write our own boot menu, just do not let the humongous file size scare you off, as a single boot menu entry now can be as simple as:

{% gist https://gist.github.com/suntong/74ad6b49ac7dca88033644f3367f636a file=grub7.cfg %}

Most of which are boilerplate that don't need to be changed. I.e., the only things that matter are the `'Debian GNU/Linux'` and the `'hd0,gpt2'`. We don't need to hunt for the UUIDs, and we don't need to remember to mount boot partition and update the boot entries any more. It can boot to whatever latest kernel it is, and it will still work even we reformat the partition and install a new Linux, and it will still work even we change the distro from Debian, to Mint, Arch, Fedora, or openSUSE etc.

There are a few more boilerplates that need to be in place so that you can only write the above few lines and everything will work out magically, which will be covered in next article, with the help of [easygen](https://github.com/go-easygen/easygen/wiki).