Fixing persistent module load Ubuntu: A Step-by-Step Guide on dark monitor
Kernel Modules
William  

Ubuntu Modules-Load.d: Autoload Kernel Modules at Boot

Put a .conf file listing your module names in /etc/modules-load.d/, one module per line, no arguments, and let systemd-modules-load.service load them at boot. That is the supported way to do a modules-load.d autoload of a kernel module on Ubuntu. Everything else – modprobe by hand, the old /etc/modules file, a custom systemd unit – is either a one-off test or a legacy path that still works but is not what you should reach for.

Last updated: 2026-07-21

The phrase "persistent module load Ubuntu" captures a common admin headache: a driver or environment script that behaves differently after a reboot. I will help you sort that out with clear steps and safe rollbacks. By documenting each change, you avoid version surprises and keep your system reliable.

Key takeaways

  • Confirm whether you mean a kernel driver or a user-space environment module before you touch anything. The same name can live in both.
  • Autoload at boot with a .conf file in /etc/modules-load.d/; block a driver with a blacklist file in /etc/modprobe.d/ plus an initramfs rebuild.
  • Use lsmod, modinfo, and modprobe -v to gather exact names and see what will actually happen.
  • Read journalctl and dmesg when a module refuses to load. The kernel usually already told you why.
  • Record the module name, the file you changed, and the command you ran, so rollbacks are simple.

What actually loads modules at boot on Ubuntu

Fixing persistent module load Ubuntu: A Step-by-Step Guide in a black server rack with blue cables

systemd-modules-load.service is the utility that runs at startup and loads modules into the kernel. It is not udev, and it is not some initscript in /etc/init.d. On boot, systemd starts this service. It reads every .conf file in the modules-load.d directories and runs the equivalent of modprobe for each module name it finds.

If you have ever asked "which utility runs at startup to load modules into the kernel?", that is the answer on any modern Ubuntu. You can watch it work with systemctl status systemd-modules-load.service. The output tells you whether it succeeded and, when it failed, which module it choked on.

This service has been the standard path since around Ubuntu 15.04, when the distribution moved to systemd. Before that, /etc/modules did the job through an init script. That file still works today because systemd honors it for compatibility, but new configuration belongs in modules-load.d.

Where modules-load.d files live and which one wins

There are three directories, and they are read in a fixed order of priority:

  1. /etc/modules-load.d/ – your local config. This is the one you edit.
  2. /run/modules-load.d/ – runtime files, usually created by other programs.
  3. /usr/lib/modules-load.d/ – files shipped by packages.

Files are read by filename in alphanumeric order across all three directories. When two files share the same name, the one in the earlier directory wins and the later copies are ignored. So a file you drop in /etc overrides a package file of the same name in /usr/lib, which is exactly what you want.

Each file must end in .conf. A file named nvidia with no extension is silently skipped, and that is a favorite way to waste twenty minutes. To disable a vendor file you cannot edit, create an empty file with the same name in /etc/modules-load.d/. That masks it without deleting anything you would have to restore later.

How do I autoload a kernel module with modules-load.d on Ubuntu?

Create one .conf file, list the module, reboot, and confirm. Here is the whole procedure.

  1. Find the exact module name first. Run modinfo <name> to prove the module exists and to read its description. A typo here means silent failure later.
  2. Create the config file. Run echo <name> | sudo tee /etc/modules-load.d/<name>.conf. The filename is arbitrary; the .conf extension is not.
  3. Put one module name per line. No modprobe options, no arguments, no comments on the same line. Comment lines start with # on their own.
  4. Reboot, or run sudo systemctl restart systemd-modules-load.service to load it now without rebooting.
  5. Confirm with lsmod | grep <name>. If the module shows up, you are done.

That is the modules-load.d autoload of a kernel module in full. If you need to pass options to the module (not just load it), modules-load.d cannot do it. Put those in an /etc/modprobe.d/<name>.conf file with an options <name> key=value line, and let modules-load.d handle the loading.

Automatic loading versus loading a module by hand

Use modules-load.d when a module must be present on every boot. Use modprobe from the shell when you are testing whether a module even works. Mixing these two up is where people lose an evening.

A modprobe command you type right now loads the module until the next reboot and then forgets it. That is the point of it. It is perfect for "does this driver bring up the hardware at all?" and useless for persistence. The declarative file in modules-load.d works the other way. It does nothing this second, but it runs on every future boot.

So the workflow is: test with modprobe, and once it works, write the name into a .conf file to make it stick. Do not write the config file first and reboot to test. You will not know whether a failure came from the module or from your config.

Loading and unloading modules with modprobe and insmod

Reach for modprobe, not insmod, in almost every case. modprobe resolves dependencies and loads whatever the module needs first. insmod takes a single .ko file path and loads only that, so if it has dependencies you get a bare unknown symbol error and nothing loads.

Common commands, and what each is for:

  • sudo modprobe <name> – load a module and its dependencies.
  • sudo modprobe -v <name> – same, but print every insmod it runs so you can see the dependency chain.
  • sudo modprobe -r <name> – unload a module and any now-unused dependencies.
  • sudo rmmod <name> – unload one module only, no dependency handling.

I use insmod only when I am loading a module I just built by hand and it lives outside the normal module tree. For everything a package installed, modprobe is the right tool. If you want a deeper walkthrough of on-demand loading, see this practical guide to dynamic module loading on Linux.

How does modprobe find a module's dependencies?

It reads modules.dep, a file in /lib/modules/$(uname -r)/. That file lists every module and the modules it depends on. modprobe walks it top to bottom so it can load prerequisites before the module you asked for.

modules.dep is not written by hand. The depmod command generates it by scanning the compiled modules in the tree. When you install a new module, the package usually runs depmod for you. If you built and copied a .ko in by hand, run sudo depmod -a yourself, or modprobe will insist the module does not exist.

If you remember the module-init-tools package, that was the old home of modprobe, depmod, lsmod, and insmod. It merged into kmod years ago, so on current Ubuntu the kmod package provides these commands. The tools behave the same; only the package name changed.

Why does a module fail to autoload even with a correct config file?

Read the logs before you guess. Run journalctl -b -u systemd-modules-load.service to see what the loader did on this boot. The output names the module it failed on and the reason, which is usually one of three things.

First, a wrong or misspelled module name. modprobe: FATAL: Module foo not found means the name in your .conf does not match any installed module. Check modinfo <name> again. Second, a missing dependency or a module built for a different kernel version, which shows up as an unknown symbol line in dmesg. Run dmesg -T | tail -20 right after the loader runs. Third, the file was ignored because it lacked the .conf extension or sat in a lower-priority directory behind a same-named file.

If systemctl status systemd-modules-load.service shows the service itself failed, the whole file had a problem, not just one line. A stray option or argument on a module line will do that, because modules-load.d only accepts bare names. For a full breakdown of that specific failure, read how to fix the "Failed to Start Load Kernel Modules" error.

Making modprobe changes permanent across reboots

A modprobe command typed at the shell never survives a reboot. That is by design. To make a module load on every boot, you write its name into a file, and you have two files to choose from.

The modern one is /etc/modules-load.d/<name>.conf, read by systemd-modules-load.service. The legacy one is /etc/modules, which you append to with a command like echo b43 | sudo tee -a /etc/modules. Both work on Ubuntu today. I use modules-load.d for new setups because it matches how the rest of systemd is configured, and I only touch /etc/modules on older boxes that already use it.

Whichever you pick, do not put both. Two files loading the same module is not harmful, but it is one more place to look when something changes. Pick one, write it down, and test with lsmod after a real reboot rather than trusting that it worked.

Blacklisting or aliasing a module instead of loading it

Stopping a module from loading is a separate job, and it lives in /etc/modprobe.d/ rather than modules-load.d. Create a file there, for example blacklist-nouveau.conf, and add two lines:

blacklist nouveau
install nouveau /bin/false

The blacklist line stops the module from auto-loading by name. The install line stops anything from pulling it in as a dependency, which a plain blacklist does not cover. You want both when you mean it.

That alone does not stop a driver that ships inside the initramfs and loads during early boot. Rebuild the initramfs so your rule is baked in: sudo update-initramfs -u. Then reboot and confirm with lsmod that the module is gone. For the corner cases and the alias directives that redirect one module name to another, see these best practices for safely blacklisting kernel modules. The full syntax also lives in the modules-load.d and modprobe.d manual pages.

FAQ

Can I put more than one module in a single modules-load.d file?

Yes. List each module on its own line in the same .conf file. The loader reads every line and loads each name in order. Keep related modules together in one file if it makes the config easier to read, or split them by purpose. There is no limit worth worrying about.

Does modules-load.d load modules early enough for root filesystem drivers?

No. systemd-modules-load.service runs after the root filesystem is already mounted, so a driver needed to mount root has to be in the initramfs instead. For those, add the module to /etc/initramfs-tools/modules and rebuild the image. modules-load.d is for modules you need after the system is up, not to boot it.

What is the difference between /etc/modules and /etc/modules-load.d?

Both load modules at boot and both take bare module names. /etc/modules is the older single-file method carried over from pre-systemd Ubuntu; the directory of .conf files is the systemd-native replacement. On current releases they coexist. I favor the directory because dropping in one file per purpose is cleaner than editing a shared list.

How do I check that my change actually persisted after a reboot?

Reboot for real, then run lsmod | grep <name> to confirm the module is loaded. For proof of who loaded it, run journalctl -b -u systemd-modules-load.service and look for your module name in this boot's log. Do not trust a restart of the service alone; a config that works at runtime can still fail at boot ordering.

Why does a module load on its own when I never configured it?

Udev auto-loads modules on demand when it detects matching hardware, so a driver can appear without any file of yours asking for it. Run modprobe -v <name> to see the dependency chain, and check dmesg -T | grep -i <name> for the device event that triggered it. If you want it gone, blacklist it in /etc/modprobe.d/ rather than hunting for a config that does not exist.