Running grub-mkconfig straight at /boot/grub/grub.cfg and hoping is how people end up staring at a rescue shell. Generate into a temporary file first, inspect it, then install it at the location your machine actually uses. Allow 10 to 20 minutes including a reboot-free verification.
The examples target the installed grub-common package, version 2.12-1ubuntu7.3, on this machine.
grub-mkconfig assembles a configuration from the settings in /etc/default/grub and the executable scripts in /etc/grub.d. By default it writes the generated text to standard output, or to a file if you pass -o. It does not install GRUB or pick a disk for you.
$ grub-mkconfig --version
grub-mkconfig (GRUB) 2.12-1ubuntu7.3
$ grub-mkconfig --help
Usage: grub-mkconfig [OPTION]
Generate a grub config file
This installed version documents exactly three options: --output=FILE, --help and --version. Do not carry over an option from a different GRUB tool.
Read the current settings and list the generator scripts. Both are ordinary, unprivileged reads on a typical install:
$ sed -n '1,220p' /etc/default/grub
$ find /etc/grub.d -maxdepth 1 -type f -printf '%f %m\n' | sort
Checkpoint: if you just changed a setting, record the old line or take a backup before proceeding. This creates a dated copy without touching the live file:
$ sudo cp --preserve=all /etc/default/grub /etc/default/grub.before-$(date +%Y%m%d-%H%M%S)
That is the first command in this guide that normally needs elevated privileges.
Write to a path in /tmp for the first run. The output file is disposable and the command never touches /boot/grub/grub.cfg on its own:
$ test -d /tmp/grub-check || mkdir /tmp/grub-check
$ sudo grub-mkconfig --output=/tmp/grub-check/grub.cfg
Generating grub configuration file ...
done
The exact messages depend on your generator scripts, so lean on the exit status rather than matching a particular line:
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ test -s /tmp/grub-check/grub.cfg && echo 'generated file is non-empty'
generated file is non-empty
Example: on this machine, running the command without elevated privileges stops with grub-mkconfig: You must run this as root. That is a permission check, not a reason to make the output file world-writable.
Look at the parts that matter for your change: the file header, menu entries, kernel paths, root identifiers, any custom entries. This is read-only inspection:
$ sed -n '1,100p' /tmp/grub-check/grub.cfg
$ grep -nE '^(menuentry|submenu|linux|initrd|search)' /tmp/grub-check/grub.cfg
A non-empty file is not proof every entry is correct. The generator can finish clean while a kernel, chainloader or custom script silently drops the entry you expected. Compare the result against /boot and against the change you intended, and fix any custom script that prints sensitive data or shell errors before you install its output.
For a custom menu item, the usual extension points are /etc/grub.d/40_custom or /boot/grub/custom.cfg, depending on your distribution's layout. Keep the first lines of 40_custom intact, and test a custom entry's paths and recovery route before relying on it: it can be boot-critical.
Warning: replacing the active GRUB configuration changes the next boot. A malformed or incomplete file can leave the machine unable to reach its normal menu. Keep the previous file until the new one is tested, and arrange physical or console access before doing this on a remote host.
Identify the configuration path your installation actually uses. A conventional Ubuntu layout puts it at /boot/grub/grub.cfg, but do not assume that for every platform:
$ sudo grub-mkconfig --output=/boot/grub/grub.cfg
Generating grub configuration file ...
done
Check the exit status, then the resulting file:
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ sudo test -s /boot/grub/grub.cfg && echo 'installed file is non-empty'
installed file is non-empty
This command only generates the configuration, it does not run grub-install. Do not follow it with grub-install unless you have a separate, verified reason to install or repair the bootloader itself.
If generation fails, the output path may be empty or incomplete. Do not reboot to see what happens. Check the error text, the syntax of /etc/default/grub, permissions on the generator scripts, and any script named in the diagnostic:
$ sudo sh -n /etc/default/grub
$ find /etc/grub.d -maxdepth 1 -type f -executable -print | sort
Recovery: if you already replaced the active file and need it back, restore from a backup made before the change, confirming first that it exists and is a regular file:
$ sudo test -f /boot/grub/grub.cfg.before-change && \
sudo cp --preserve=all /boot/grub/grub.cfg.before-change /boot/grub/grub.cfg
That restore changes boot configuration again, so check its exit status and keep console access available. With no known-good backup, stop changing files and use your distribution's documented boot-repair procedure from local or rescue media.
/etc/default/grub and the executable generator scripts were checked.