LinuxX · Kernel ManagementKernel command line
Kernel command line parameters - the settings you can only make at boot
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
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
| Mechanism | Applied when | Changeable at runtime | Example |
|---|---|---|---|
| Kernel command line | At boot, before init | No — needs a reboot | intel_iommu=on |
Module parameter (modprobe.d) | When the module is loaded | Only if the module is unloadable | options kvm_intel nested=1 |
sysctl | Any time | Yes | vm.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 inlinux-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.
$ 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:
$ modinfo -p nvme_coremultipath: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
Q1. Why can 1 GB huge pages only be reserved on the kernel command line?
Q2. The kernel rejects a command line parameter it does not recognise, so a typo will prevent the host from booting.
Q3. Which statements about setting a module parameter are correct? Select all that apply.
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.