Load a Kernel Module at Boot with systemd-modules-load

systemd-modules-load loads a kernel module before anything else starts, which matters when hardware only shows up once its driver is in. This guide uses systemd 255.4 from Ubuntu package 255.4-1ubuntu8.17, with the installed systemd-modules-load.service and its modules-load.d(5) configuration.

Allow about fifteen minutes, plus a reboot if you need to verify the real boot path. You need a shell, a module name that exists for your running kernel, and sudo access. Loading a kernel module changes live kernel state. Check the module's purpose and dependencies first, and do not use an arbitrary name copied from an untrusted source.

1. Check the service and choose a real module

Start with read-only checks before you write anything. The service is a oneshot unit that runs early in boot and settles into the active (exited) state once it has done its job. The helper binary is installed at /usr/lib/systemd/systemd-modules-load on this machine.

$ systemctl status --no-pager systemd-modules-load.service
$ /usr/lib/systemd/systemd-modules-load --version
$ uname -r
$ modinfo MODULE_NAME

Replace MODULE_NAME with the module you actually need, without the .ko suffix. For example, the manpage uses virtio-net in its configuration example, while modinfo virtio-net must succeed on your host before you use that name. A module name is not a package name, and it is not necessarily the same as the hardware device name printed by lspci or similar tools.

Checkpoint: stop here if modinfo cannot find the module. A boot configuration file cannot conjure a module that is not there. Chase down the kernel package, hardware support or the module's documented installation path before going any further.

2. Create a local modules-load.d file

Put administrator-owned configuration in /etc/modules-load.d/. Create one descriptive file with a .conf suffix. Each non-comment line names one module, and blank lines or lines starting with # or ; are ignored.

$ sudo install -d -m 0755 /etc/modules-load.d
$ sudo tee /etc/modules-load.d/60-local-example.conf >/dev/null <<'EOF'
# Load the module needed by the local system
MODULE_NAME
EOF

Swap in your real module name before running that command. For the manpage's example, the file would just contain virtio-net. Keep the file root-owned and system-readable, and do not put modprobe options here: this directory is only a list of names. Options live in the separate modprobe.d(5) system.

Check the exact file before you rely on it:

$ sudo sed -n '1,20p' /etc/modules-load.d/60-local-example.conf
$ sudo find /etc/modules-load.d -maxdepth 1 -type f -name '*.conf' -printf '%f\n' | sort

Files are read from /etc/, /run/, /usr/local/lib/ and /usr/lib/, and a higher-priority directory overrides a same-named file in a lower one. Every file is sorted lexicographically by filename, which is why the two-digit prefix convention exists: it makes the ordering visible. It is not a general option-merging system, though. A same-named file in /etc/ replaces the lower-priority file outright, it does not merge with it.

3. Apply the setting without rebooting

The whole point of this configuration is usually the next boot, but you can test it now by restarting the oneshot service. That is an elevated, state-changing step: it tells the kernel to load the listed modules immediately, and it can fail if a module is incompatible or its dependencies are missing.

$ sudo systemctl restart systemd-modules-load.service
$ systemctl status --no-pager systemd-modules-load.service

Success looks like Active: active (exited). Worth knowing: a restart never unloads a module that was already present, and removing the configuration file later will not unload it either. Do not follow this guide with modprobe -r as an automatic undo step; unloading a module can break whatever else is using the device or a dependent module.

For a clean checkpoint, ask the kernel directly whether the module is present:

$ grep -w '^MODULE_NAME ' /proc/modules
$ printf 'module check status: %s\n' "$?"

Use the same name you put in the file. A matching line means it loaded; nothing back means this check did not find it. Module names sometimes get hyphens and underscores normalised differently by tooling, so cross-check with lsmod if the spelling looks ambiguous:

$ lsmod | grep -E '^(MODULE_NAME|MODULE_NAME_WITH_UNDERSCORES)([[:space:]]|$)'

4. Verify the boot-time path

If the module genuinely needs to be present before ordinary services start, a successful manual restart is not proof of that. Reboot during a maintenance window instead. Rebooting is service-disrupting and can interrupt remote access, so save your work and make sure you have console or out-of-band recovery before you do it.

$ sudo reboot

Once the machine is back, check both the unit and the kernel state:

$ systemctl status --no-pager systemd-modules-load.service
$ lsmod | grep -E '^(MODULE_NAME|MODULE_NAME_WITH_UNDERSCORES)([[:space:]]|$)'
$ journalctl -b -u systemd-modules-load.service --no-pager

The journal will show whether the service completed cleanly or hit a module-loading error. If the unit is skipped or failed, read the unit conditions and the journal rather than mashing restart. The installed unit only has anything to do when the kernel can load modules and at least one relevant configuration directory or kernel command-line trigger exists.

5. Use a kernel command-line list only for early boot needs

The service also accepts modules_load= as a comma-separated list on the kernel command line, and rd.modules_load= is read in the initrd only. These earn their keep when a module is needed before the normal root filesystem is even available, but changing bootloader kernel arguments affects the very next boot and can make a host unbootable if the module name or syntax is wrong.

Prefer the /etc/modules-load.d/ file for an ordinary persistent requirement, and reach for a kernel command-line parameter only when the initrd or boot process genuinely needs it, done through your distribution's documented bootloader workflow. Do not stack both forms just to make a failed load feel more likely to succeed; duplicate requests will not fix a missing or incompatible module.

6. Remove the persistent request

To undo this guide, remove only the file you created. Confirm the path first: deleting a vendor file or another administrator's configuration could change boot behaviour for the whole machine, not just your test.

$ sudo sed -n '1,20p' /etc/modules-load.d/60-local-example.conf
$ sudo rm /etc/modules-load.d/60-local-example.conf
$ test ! -e /etc/modules-load.d/60-local-example.conf && echo 'persistent request removed'

This stops the module being requested by that file on a future boot. It does not unload a module already present in the running kernel. If it must come out of the current boot too, treat that as its own maintenance decision: inspect dependants with lsmod and use your system's normal change procedure. Never unload a module purely to make a test look tidy.

Common failure boundaries

Done means