Kernel modules — loading, inspecting, and blacklisting
The kernel loads driver and feature support as modules instead of compiling everything in. lsmod/modinfo inspect loaded modules; modprobe loads with dependency resolution; rmmod unloads. Auto-load via /etc/modules-load.d; blacklist via /etc/modprobe.d.
You attach a USB NIC to a server that cannot reach its primary interface. The interface appears and disappears. dmesg shows the driver loading, failing, loading again. The root cause: a module parameter defaults to “auto-negotiate” but the switch at the other end has negotiation disabled. One line — modprobe r8169 speed=100 duplex=full — and the interface stabilises. Kernel modules are the mechanism by which Linux avoids compiling every driver for every piece of hardware into a single monolithic binary. Instead, drivers and features ship as separate .ko files that the kernel can load and unload at runtime. Understanding this mechanism is how you fix driver problems, tune module behaviour, and prevent a problematic module from loading at all.
After this lesson you can inspect loaded modules with lsmod and modinfo, load and unload them with modprobe and rmmod, pass module parameters, configure auto-loading via /etc/modules-load.d/, and prevent a module from loading by blacklisting it in /etc/modprobe.d/.
Inspecting what is loaded: lsmod and modinfo. lsmod reads /proc/modules and formats it for human consumption. modinfo queries the module metadata from the .ko file itself.
# List all currently loaded modules:
lsmod
# Module Size Used by
# nf_conntrack 172032 2 nf_nat,nf_conntrack_netlink
# ip_tables 36864 1 iptables
# dm_crypt 49152 0
# ...
# Columns: module name | size in bytes | use-count | dependents
# Inspect a specific module:
modinfo ext4
# filename: /lib/modules/6.1.0-21-amd64/kernel/fs/ext4/ext4.ko
# description: Fourth Extended Filesystem
# author: Remy Card, Stephen Tweedie, Andrew Morton, ...
# license: GPL
# depends: mbcache,jbd2
# retpoline: Y
# parm: delayed_allocation:Use delayed allocation during write (bool)
# parm: ...
# Find the .ko file on disk:
modinfo -n bluetooth
# /lib/modules/6.1.0-21-amd64/kernel/net/bluetooth/bluetooth.ko
# List module parameters (parm lines):
modinfo r8169 | grep parm
# parm: debug:Debug verbosity level (0=none,...,16=all) (int)The Used by count tells you whether it is safe to remove a module. A count of 0 means nothing depends on it; a count > 0 means other loaded modules or open devices still need it. rmmod refuses to unload a module with a non-zero use count.
Loading modules with modprobe. modprobe is the correct tool for loading modules — unlike insmod (which requires the full path and ignores dependencies), modprobe reads the dependency database at /lib/modules/$(uname -r)/modules.dep and loads prerequisites automatically.
# Load a module (and its dependencies):
sudo modprobe nf_conntrack
# (no output on success)
# Verify it loaded:
lsmod | grep nf_conntrack
# nf_conntrack 172032 0
# Load with parameters:
sudo modprobe r8169 debug=1
# Sets the debug parameter for the r8169 driver at load time
# List a module's dependencies without loading:
modprobe --show-depends nf_nat
# insmod /lib/modules/.../kernel/net/netfilter/nf_conntrack.ko
# insmod /lib/modules/.../kernel/net/netfilter/nf_nat.ko
# (modprobe would load nf_conntrack first, then nf_nat)
# Dry-run (show what would happen, do not load):
modprobe --dry-run --verbose tcp_bbrModule parameters passed via modprobe apply for this one load. For permanent parameters, use /etc/modprobe.d/ (covered in Step 4).
Unloading modules with rmmod. An unloaded module frees its memory. This is used during driver debugging (unload, change parameter, reload) and when a module causes instability. Unloading fails if the use count is non-zero.
# Unload a module (fails if anything depends on it):
sudo rmmod nf_conntrack
# rmmod: ERROR: Module nf_conntrack is in use by: nf_nat nf_conntrack_netlink
# → You must unload dependents first
# modprobe -r handles the dependency chain automatically:
sudo modprobe -r nf_nat
# Unloads nf_nat, then nf_conntrack (if no other dependents remain)
# Verify it is gone:
lsmod | grep nf_conntrack
# (no output)
# GOTCHA: some modules cannot be unloaded because something in the kernel
# holds a reference that the use-count doesn't capture cleanly.
# In that case, 'modprobe -r' returns "FATAL: Module ... is in use".
# The only safe option is a reboot. Never use 'rmmod -f' (force) on a
# production system — it bypasses the use-count check and can kernel-panic.Persistent loading via /etc/modules-load.d and parameters via /etc/modprobe.d.
# Auto-load a module at every boot:
# Drop a .conf file in /etc/modules-load.d/ (one module name per line):
echo "tcp_bbr" | sudo tee /etc/modules-load.d/bbr.conf
# systemd-modules-load.service reads this at boot
# Apply now without rebooting:
sudo modprobe tcp_bbr
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
# Set permanent module parameters via /etc/modprobe.d/:
sudo tee /etc/modprobe.d/r8169.conf << 'EOF'
# Fix for switch that has auto-negotiation disabled
options r8169 speed=100 duplex=1
EOF
# Parameters take effect the next time the module loads
# (reboot or: modprobe -r r8169 && modprobe r8169)
# Blacklist a problematic module so it never loads:
sudo tee /etc/modprobe.d/blacklist-pcspkr.conf << 'EOF'
# Disable the PC speaker (beep) driver — it causes spurious alerts
blacklist pcspkr
EOF
# GOTCHA: blacklisting does NOT remove an already-loaded module.
# It only prevents future loads. If it is already loaded:
sudo modprobe -r pcspkr
# Then the blacklist prevents it from auto-loading on next boot.
# Verify the blacklist is in effect:
cat /etc/modprobe.d/blacklist-pcspkr.conf
# blacklist pcspkr
lsmod | grep pcspkr
# (no output — module is not loaded)There is a subtlety: blacklist in /etc/modprobe.d/ prevents modprobe from loading the module automatically, but it does not prevent another module from explicitly loading it as a dependency. For absolute prevention, use install <module> /bin/false in the same file — this makes any load attempt (including via dependencies) invoke /bin/false, which fails.
When a blacklist does not take effect. A common production puzzle: you blacklisted a module, it still loads. The culprit is usually the initramfs.
# The initramfs is the early boot environment that mounts the real root FS.
# It has its own copy of module configs. If your blacklist isn't in there,
# the module loads before /etc/modprobe.d/ is even mounted.
# After creating a blacklist file, rebuild the initramfs:
sudo update-initramfs -u
# update-initramfs: Generating /boot/initrd.img-6.1.0-21-amd64
# Reboot and verify:
lsmod | grep <module-name>
# (should be absent now)
# Confirm which modules are included in the initramfs:
lsinitramfs /boot/initrd.img-$(uname -r) | grep -i <module-name>
# If the module is needed by the initramfs itself (e.g. a filesystem driver),
# you cannot safely blacklist it — the system will fail to boot.
# In that case, use a kernel parameter instead:
# Add to /etc/default/grub GRUB_CMDLINE_LINUX: module_blacklist=nouveau
# Then: sudo update-grubScenario: a Nouveau GPU driver is causing kernel panics; switch to a blacklist so the proprietary driver can take over.
# Step 1: Confirm nouveau is loaded and causing problems:
lsmod | grep nouveau
# nouveau 2166784 0
dmesg | tail -20 | grep -i nouveau
# [ 15.321] nouveau 0000:01:00.0: bus: MMIO read of 00000000 FAULT ...
# Step 2: Try to unload it now:
sudo modprobe -r nouveau
# modprobe: FATAL: Module nouveau is in use.
# Something (X server, GDM) holds a reference.
# Step 3: Blacklist it so it will not load at next boot:
sudo tee /etc/modprobe.d/blacklist-nouveau.conf << 'EOF'
blacklist nouveau
options nouveau modeset=0
EOF
# Step 4: Rebuild initramfs so the blacklist is applied before / is mounted:
sudo update-initramfs -u
# update-initramfs: Generating /boot/initrd.img-6.1.0-21-amd64
# Step 5: Reboot:
sudo reboot
# Step 6: After reboot, confirm nouveau is absent:
lsmod | grep nouveau
# (no output — blacklist is effective)
# Step 7: Install and load the proprietary driver (e.g. NVIDIA):
sudo apt install nvidia-driver
# The installer handles modprobe for nvidia and nvidia_uvm automatically
lsmod | grep nvidia
# nvidia_uvm 65536 0
# nvidia 21397504 1 nvidia_uvmKey lesson: blacklist alone is insufficient when the module loads from initramfs. update-initramfs -u is the step people miss, leading to “I blacklisted it but it still loads” confusion.
▸Common mistake
Using rmmod -f (force) on a production system. The -f flag bypasses the use-count check and removes the module even if other code has references to it. The result is a kernel that attempts to call functions in memory that no longer exists — leading to kernel panics or silent data corruption. There is almost never a legitimate reason to use -f on a running production system. The correct approach: find what is using the module with lsmod (the third column shows dependents), unload the dependents first, then unload the module cleanly.
▸Why this works
macOS has no kernel modules in the Linux sense. macOS uses kernel extensions (kexts) on older versions, and DriverKit (user-space drivers) on newer ones. The kextload/kextunload commands exist but are being phased out. More importantly, macOS’s System Integrity Protection (SIP) prevents loading unsigned kexts on modern hardware. The Linux module system is significantly more permissive — any root user can load any compiled .ko file — which is both a flexibility advantage and a security consideration (a compromised root can load a rootkit module). Linux Security Modules (SELinux, AppArmor) can restrict module loading, but the default is open.
You add `blacklist pcspkr` to /etc/modprobe.d/blacklist.conf. After the next reboot, `lsmod | grep pcspkr` still shows the module loaded. What is the most likely reason?
The kernel ships drivers and features as separate .ko module files rather than a single monolithic binary, allowing runtime loading and unloading. Inspect loaded modules with lsmod (list, with use-count and dependents) and modinfo (metadata, parameters, dependencies). Load with modprobe (resolves dependency chain automatically); pass parameters as modprobe <name> param=value. Unload with modprobe -r <name> (handles dependents) or rmmod (single module, fails if use-count > 0 — never use -f on production). Auto-load on boot: add module names to /etc/modules-load.d/<name>.conf. Persistent parameters: options <module> param=value in /etc/modprobe.d/<name>.conf. Blacklist: blacklist <module> in /etc/modprobe.d/; if the module is in the initramfs, also run sudo update-initramfs -u or the blacklist has no effect at early boot.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.