Skip to main content
RunBook Academy

LinuxX · Kernel ManagementKernel command line

Kernel command line parameters - the settings you can only make at boot

Intermediate⏱ ~14 minmodinfosysctldmesg

What you'll learn

  • Decide whether a given kernel setting belongs on the command line, in modprobe.d, or in sysctl
  • Explain how the kernel routes command line tokens to itself, to modules and to init
  • Detect a mistyped parameter instead of discovering it months later
  • Verify a module parameter took effect through /sys/module

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11

Not yet marked complete on this device.

linux-bootloader-grub covers how to get a parameter onto the kernel command line: edit /etc/default/grub, regenerate, or use grubby on a BLS host. This lesson is about the other half — what the kernel does with the line once it has it, which settings genuinely require it, and how to prove one worked.

The reason this matters is that operators reach for sysctl first, because sysctl is discoverable and takes effect immediately. A significant set of kernel behaviour is simply not reachable that way. Memory is partitioned, CPUs are isolated and IOMMU groups are formed long before userspace exists, so those decisions can only be expressed on the command line.

Three places a kernel knob can live

MechanismApplied whenChangeable at runtimeExample
Kernel command lineAt boot, before initNo — needs a rebootintel_iommu=on
Module parameter (modprobe.d)When the module is loadedOnly if the module is unloadableoptions kvm_intel nested=1
sysctlAny timeYesvm.swappiness=10

Picking the wrong one is not an error that announces itself. sysctl -w on a key that does not exist fails loudly, which is fine — but writing a modprobe.d option for a built-in module succeeds silently and does nothing at all.

What belongs on the command line

The common cases, all of which decide something before userspace exists:

  • intel_iommu=on / amd_iommu=on / iommu=pt — device isolation groups, needed for passthrough.
  • hugepagesz=1G hugepages=16 — 1 GB pages must be reserved from contiguous memory at boot; there is no runtime path.
  • isolcpus=, nohz_full=, rcu_nocbs= — CPU isolation for latency-sensitive workloads; the scheduler domains are built at boot.
  • crashkernel= — memory reserved for the kdump kernel.
  • mitigations= — speculative execution defences.
  • console= — where kernel messages go, covered in linux-serial-console-and-sol.
  • net.ifnames=0 — the interface naming scheme, decided as devices are enumerated.

Note that hugepagesz/hugepages is a partial case worth knowing: 2 MB pages can be allocated at runtime through vm.nr_hugepages, and often fail to be, because after some uptime there is no contiguous 2 MB region left. 1 GB pages have no runtime path at all. “Reserve at boot” is the reliable answer for both.

How the kernel splits the line

The kernel does not treat the command line as one namespace. It sorts each token into one of four buckets:

BOOT_IMAGE=/boot/vmlinuz-6.8.0-51-generic  root=UUID=b043... ro
  crashkernel=512M  nvme_core.io_timeout=255  systemd.unit=rescue.target  single

  crashkernel=512M          -> a parameter the kernel itself knows
  nvme_core.io_timeout=255  -> module.parameter form, routed to the nvme_core module
  systemd.unit=...          -> unrecognised: exported as environment for init
  single                    -> unrecognised bare word: passed as an argument to init

The module.parameter=value form is the important one. It works whether the module is built into the kernel or loaded later, which makes it the only way to configure a built-in module — and it is the reason a parameter can look like a kernel parameter when it is really a module one.

Everything the kernel does not recognise is handed to userspace rather than rejected. That is what makes systemd.unit= work without the kernel knowing anything about systemd. It is also what makes typos invisible.

Read-only / Safecmdline verification
$ cat /proc/cmdline
sudo dmesg | grep -i 'command line'
BOOT_IMAGE=/boot/vmlinuz-6.8.0-51-generic root=UUID=b0434124-e13c-4dff-9006-5abf403266fe ro crashkernel=512M nvme_core.io_timeout=255 mitigations=auto

[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.0-51-generic root=UUID=b0434124... ro crashkernel=512M nvme_core.io_timeout=255 mitigations=auto
[    0.036142] Unknown kernel command line parameters "nvme_core.io_timout=255", will be passed to user space.

Illustrative output

Module parameters: three ways in, one way to check

modinfo -p lists what a module accepts, with types and descriptions. Run it before inventing a parameter name:

Read-only / Safemodinfo -p
$ modinfo -p nvme_core
multipath:turn on native support for multiple controllers per subsystem
iopolicy:Default multipath I/O policy; 'numa' (default), 'round-robin' or 'queue-depth'
admin_timeout:timeout in seconds for admin commands (uint)
io_timeout:timeout in seconds for I/O (uint)
shutdown_timeout:timeout in seconds for controller shutdown (byte)

Illustrative output

The three ways to set one:

# 1. At load time, for a module loaded after boot
sudo modprobe nvme_core io_timeout=255

# 2. Persistently, for a loadable module
echo 'options nvme_core io_timeout=255' | sudo tee /etc/modprobe.d/nvme.conf

# 3. On the kernel command line, which works for built-in modules too
#    nvme_core.io_timeout=255

And the one way to check, regardless of which you used:

cat /sys/module/nvme_core/parameters/io_timeout

A handful of parameters are writable at runtime through that same path — the file is mode 0644 rather than 0444 when the module author allowed it. Most are read-only, and a read-only one requires unloading and reloading the module, which for a storage or network driver means it is effectively a reboot.

Rolling out a command line change

The command line is fleet-wide state that only takes effect at the next boot, which makes it unusually easy to get into an inconsistent condition: half the fleet carries the parameter because it rebooted, half does not.

1. Decide the mechanism. Is it a sysctl? Then it is not this.
   Is the module built-in? Then modprobe.d is not an option.
2. Change it on ONE host. Reboot it.
3. cat /proc/cmdline                        parameter present
4. dmesg | grep 'Unknown kernel command line'   nothing you added
5. cat /sys/module/<mod>/parameters/<param>     the value you set
6. Verify the behaviour you wanted actually changed.
7. Only then roll it out - and track it as a campaign, because
   hosts do not have it until they reboot.

Step 6 is separate from step 5 for a reason. A parameter can be present, correctly spelled and correctly applied and still not do what you expected, because the documentation was for a different kernel version or the effect is conditional on hardware you do not have.

Knowledge check

Knowledge check · 4 questions

  1. Q1. Why can 1 GB huge pages only be reserved on the kernel command line?

  2. Q2. The kernel rejects a command line parameter it does not recognise, so a typo will prevent the host from booting.

  3. Q3. Which statements about setting a module parameter are correct? Select all that apply.

  4. Q4. You added a module parameter on the kernel command line and rebooted. cat /proc/cmdline shows it exactly as you typed it. What is the strongest evidence that it actually took effect?

Passing score: 75%. Answers are checked in this browser.