Learn › RHCSA (EX200) › Deploy, Configure, Maintain
grubby - a hands-on Linux lab on a real virtual machine.
Make boot changes that persist. Read the current kernel with grubby --default-kernel and grubby --info=DEFAULT, then change the kernel command line permanently and BLS-aware with grubby --update-kernel=ALL --args= and --remove-args= so it survives kernel updates. Set menu defaults like GRUB_TIMEOUT and GRUB_CMDLINE_LINUX in /etc/default/grub and regenerate with grub2-mkconfig -o /boot/grub2/grub.cfg, then confirm the running kernel with cat /proc/cmdline. Serves EX200 deploy-configure: modify the system bootloader.
A monitoring task needs the server to boot with an extra kernel option turned on. You reboot, hit the GRUB menu, press e, and type the flag onto the kernel line. It boots. It works. You mark the job done and move on.
Then the machine gets a routine kernel update, reboots on its own, and your flag is gone. The edit you made at the GRUB menu was a one-time, in-memory change: it never touched disk, and the new kernel knew nothing about it. This lesson teaches the tool that makes a boot change stick: written to disk, and carried forward automatically every time a new kernel is installed. On the exam, a boot flag that does not survive a reboot scores zero.
The black boxes below are a practice terminal: a safe sandbox that checks the one command each step teaches. The outputs are from a real RHEL 10 machine (AlmaLinux 10.2). A few values are specific to that machine: the kernel version 6.12.0-211.7.3.el10_2.x86_64 and the resume=UUID=... argument depend on the exact build and disk, so your machine will print different ones. What is stable everywhere is the SHAPE: grubby reports and edits the kernel command line, /etc/default/grub holds the menu defaults, and /proc/cmdline shows what the running kernel actually booted with.
When you power on a Linux server, a small program called the bootloader runs first. On RHEL it is GRUB, the GRand Unified Bootloader. Its job is to find a kernel, hand it a line of options, and start it. That line of options is the kernel command line: a string of settings like ro (mount root read-only at first) and quiet (show fewer boot messages) that shape how the system comes up.
You could edit GRUB's files by hand, but the layout is fiddly and easy to break. grubby is the tool that does it safely for you. Think of it as a remote control for the bootloader: you tell it add this argument or which kernel boots by default, and it edits the right files in the right places. On RHEL 10 those files follow the BLS layout (BootLoader Specification), one small config snippet per installed kernel, and grubby understands that layout so you do not have to.
You reach for grubby whenever a boot setting has to persist. Common real cases: turning on a serial console for remote access, adding a debug flag while chasing a boot hang, or setting a memory limit for testing. The exam objective is blunt about it: modify the system bootloader, and make the change survive.
The reason grubby beats hand-editing is the kernel-update problem from the hook. When dnf installs a new kernel, it writes a brand-new boot entry. If your change lived only in the old entry, the new kernel boots without it. grubby --update-kernel=ALL writes your change into every entry, present and future, so the next kernel inherits it. That is the whole reason the tool exists.
Before you change anything, find out what boots today. grubby --default-kernel prints the path of the kernel GRUB starts by default. It is the safe first move: it names your target without touching a thing.
Ask which kernel is the default:
grubby --default-kernel
prompt: [root@servera ~]# answer: grubby --default-kernel output: /boot/vmlinuz-6.12.0-211.7.3.el10_2.x86_64 hint: The bootloader tool is grubby; the flag that names the default kernel is --default-kernel.
The output is a single file path: /boot/vmlinuz- followed by the kernel version. vmlinuz is the compressed kernel image, and everything after it, 6.12.0-211.7.3.el10_2.x86_64, is the version string. This is the kernel GRUB will boot unless you tell it otherwise. You have not changed anything; you have only asked, which is exactly how you want to start any bootloader task.
Your kernel version will not be 6.12.0-211.7.3.el10_2. That is the exact build on the machine these outputs came from. On your system the numbers after vmlinuz- will differ, and they change again every time a kernel update lands. Read the shape, a /boot/vmlinuz-<version> path, not the exact digits.
--default-kernel names the kernel; --info=DEFAULT shows everything about it, including the full list of arguments it boots with. DEFAULT is a keyword that means the default entry, so you do not have to paste the long kernel path. The output has a kernel= line and, the part you care about, an args= line holding the whole kernel command line.
Read the default entry in full:
grubby --info=DEFAULT
prompt: [root@servera ~]# answer: grubby --info=DEFAULT output: kernel="/boot/vmlinuz-6.12.0-211.7.3.el10_2.x86_64" args="ro crashkernel=2G-64G:256M,64G-:512M resume=UUID=... rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap console=ttyS0,115200 console=tty0" hint: Same grubby tool; ask for the default entry with --info=DEFAULT.
Two lines matter here. kernel= repeats the path you already saw. args= is the real prize: it is the exact kernel command line for this entry, in quotes. Read a few of them. ro mounts the root filesystem read-only during early boot. crashkernel=... reserves memory for a crash dump. resume=UUID=... names the swap area to resume from after hibernation. rd.lvm.lv=rhel/root tells the early boot to activate that LVM volume. console=ttyS0,115200 and console=tty0 send boot messages to both a serial line and the screen. When a task says add or remove a boot argument, this args= string is what you are editing.
The resume=UUID=... value is specific to this machine's swap area, so yours will show a real, different UUID in place of the .... The rest of the arguments are typical RHEL 10 defaults and should look much like yours. You are never asked to memorize this string; you read it to see what is currently set before you add or remove anything.
Now the change that sticks. To add a kernel argument to every boot entry, present and future-facing, you run grubby --update-kernel=ALL --args='<arg>'. The --update-kernel=ALL part is the magic: ALL means write this into every installed kernel entry, so a future kernel update inherits it too. The --args= part is the argument to add, in quotes.
Add the quiet argument to every entry so boot messages are hidden:
grubby --update-kernel=ALL --args='quiet'
prompt: [root@servera ~]# answer: grubby --update-kernel=ALL --args='quiet' output: hint: grubby with --update-kernel=ALL targets every entry; --args='quiet' is the argument to add.
No output, which is success: grubby is silent when the edit lands. It just rewrote the args= line of every boot entry to include quiet, and it saved that to disk. Because you targeted ALL, the next kernel dnf installs will carry quiet too. This is BLS-aware editing: grubby touched the per-kernel BLS snippets in /boot/loader/entries/, not a single monolithic file. Run grubby --info=DEFAULT again and you would see quiet now sitting in the args= string.
--args adds; it does not replace. To change the VALUE of an existing argument, remove the old one and add the new one. Otherwise grubby leaves two copies on the line and the last one usually wins in a way you did not intend. And always prefer --update-kernel=ALL over targeting one kernel by path. Editing only the current kernel is the classic exam mistake: it boots fine today, then the next kernel update quietly drops your change and the check fails.
The mirror image of adding is removing. grubby --update-kernel=ALL --remove-args='<arg>' strips an argument from every boot entry. Same ALL target, so the removal is permanent across future kernels, not just the one booted now.
Take the quiet argument back off every entry:
grubby --update-kernel=ALL --remove-args='quiet'
prompt: [root@servera ~]# answer: grubby --update-kernel=ALL --remove-args='quiet' output: hint: Same grubby --update-kernel=ALL, but this time --remove-args='quiet' strips it out.
Silence again means it worked: grubby rewrote every entry's args= line with quiet gone and saved it. --args and --remove-args are the two halves of the same tool, and you will often use them together to change a value: remove the old argument, add the new one, in a single command line or two. Targeting ALL on the remove keeps present and future entries in sync.
There is a difference between what is written on disk and what the running kernel actually booted with. grubby --info reads the config; /proc/cmdline reads the truth. It is a special file the kernel exposes showing the exact command line it received at boot. Read it to confirm the running system, and remember: changes you make with grubby only take effect on the NEXT reboot, so /proc/cmdline shows the previous state until then.
Read the live kernel command line:
cat /proc/cmdline
prompt: [root@servera ~]# answer: cat /proc/cmdline output: BOOT_IMAGE=(hd0,gpt2)/vmlinuz-6.12.0-211.7.3.el10_2.x86_64 root=/dev/mapper/rhel-root ro crashkernel=... rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap console=ttyS0,115200 console=tty0 hint: It is a plain file read: cat the special file /proc/cmdline.
This one line is what the kernel is running on right now. BOOT_IMAGE= shows which image GRUB loaded and from which partition, (hd0,gpt2). root=/dev/mapper/rhel-root names the root device. After that come the same familiar arguments you saw in grubby --info: ro, crashkernel, the LVM activations, and the two console= lines. The key discipline: after editing with grubby, you reboot, then cat /proc/cmdline to confirm your change is actually present in the running kernel. If it is not there, the edit did not take, or you have not rebooted yet.
grubby edits per-kernel arguments. Two settings live somewhere else: the GRUB menu itself. The file /etc/default/grub holds the defaults, and the two lines the exam cares about are GRUB_TIMEOUT, how many seconds the menu waits before booting the default, and GRUB_CMDLINE_LINUX, arguments applied to every entry GRUB generates. Read the timeout:
grep GRUB_TIMEOUT= /etc/default/grub
prompt: [root@servera ~]# answer: grep GRUB_TIMEOUT= /etc/default/grub output: GRUB_TIMEOUT=5 hint: grep for the setting name GRUB_TIMEOUT= inside the file /etc/default/grub.
GRUB_TIMEOUT=5 means the boot menu pauses five seconds before it auto-boots the default kernel. Set it higher to have more time to pick an entry, or to 0 to boot instantly with no menu. But editing this file does nothing on its own: /etc/default/grub is a source file, and GRUB does not read it at boot. You must regenerate the real config GRUB actually uses. On RHEL 10 that command is grub2-mkconfig -o /boot/grub2/grub.cfg, which reads /etc/default/grub, expands it, and writes the live grub.cfg. Forgetting that regenerate step is the single most common /etc/default/grub mistake.
Two different worlds, do not mix them up. Changes made with grubby --args take effect on their own; you do NOT run grub2-mkconfig for those. Changes to /etc/default/grub, like GRUB_TIMEOUT or GRUB_CMDLINE_LINUX, do NOTHING until you regenerate with grub2-mkconfig -o /boot/grub2/grub.cfg. Edit /etc/default/grub and skip the regenerate, and your change silently never happens. That single missed command has failed more exam attempts than any typo.
Scaffolding off. No command is printed this time. You have every piece you need.
A task hands you this: the server must boot with the argument quiet on the kernel command line, and the change must survive future kernel updates. You are not going to hand-edit files, and you are not going to make a one-time menu edit that vanishes. You want the tool that writes the argument into every boot entry, present and future, in one command. Which single command adds quiet to all entries so it persists across kernel updates?
prompt: [root@servera ~]# answer: grubby --update-kernel=ALL --args='quiet' output: hint: The tool is grubby. Target every entry with --update-kernel=ALL, then --args='quiet'.
grubby --update-kernel=ALL --args='quiet' is the answer, and each piece earns its place. grubby is the safe bootloader editor. --update-kernel=ALL writes to every installed entry, which is what makes it survive the next kernel update. --args='quiet' is the argument you are adding. It prints nothing and takes effect on the next reboot, where cat /proc/cmdline will show quiet in the running line. Targeting one kernel by path would have booted fine today and failed the moment a new kernel arrived. ALL is what earns the persist.
You earned this cheat sheet. Every row is a form you just ran or built:
The one thing to burn in for the exam: for kernel arguments, use grubby --update-kernel=ALL so the change survives kernel updates, and it takes effect on its own. For /etc/default/grub settings like GRUB_TIMEOUT, you MUST run grub2-mkconfig -o /boot/grub2/grub.cfg or nothing happens.
After any boot change, reboot and confirm with cat /proc/cmdline. That file is the single source of truth for what the running kernel booted with. If your argument is not in that line after a reboot, the change did not take, and on the exam an unverified boot change is a boot change you cannot trust.
The practice terminal has walked you through the whole loop. grubby --default-kernel and --info=DEFAULT to read what boots today, --update-kernel=ALL --args= and --remove-args= to change the kernel command line so it persists, /etc/default/grub plus grub2-mkconfig for the menu defaults, and cat /proc/cmdline to prove what the running kernel actually booted with. Every one of those you typed yourself.
The deploy-configure module capstone, Operation Rollout, is where you run these against a real RHEL 10 machine. A full VM boots for you, with its own real kernel entries and its own /etc/default/grub. The mission hands you objectives that use exactly what you practiced here. Add a kernel argument that survives updates, adjust the boot timeout and regenerate, then verify the running kernel. One difference from this lesson: the mission shows no commands. You read the objective, recall the grubby --update-kernel=ALL and grub2-mkconfig forms, and type them. That recall is what makes it stick on exam day.
Finish the other deploy-configure lessons, then go modify a real bootloader in Operation Rollout.
Practice GRUB Modifications in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.