How to Use DKMS to Build Kernel Modules on Fedora

Use the dkms add, build, and install commands to register and compile your kernel module for automatic updates on Fedora.

You updated the kernel and your custom driver vanished

You applied a Fedora kernel update and your proprietary GPU driver or custom hardware module stopped working. The system boots, but the device shows up as unknown in lspci or lsusb. You compiled the module manually last time, but the new kernel headers shifted the ABI and the old .ko file is incompatible. Compiling it from scratch every time a kernel ships is tedious and breaks the moment you forget to run make install after an update. DKMS automates that rebuild process so your module stays in sync with the kernel lifecycle.

Rebuild the module before you reboot. A missing driver breaks more than just convenience.

What DKMS actually does under the hood

Kernel modules are compiled against specific kernel headers. When Fedora ships a new kernel, the internal structures and function signatures change slightly. Out-of-tree modules break because they were built against the old headers. DKMS acts as a build hook that lives between your source tree and the kernel update process. It registers your module source directory, tracks the current kernel version, and automatically triggers a rebuild whenever a new kernel package installs.

Think of it like a version-controlled recipe that re-cooks itself every time you swap out the main ingredients. The module stays in /lib/modules/$(uname -r)/extra/ and loads automatically on boot. DKMS does not replace the kernel build system. It wraps make and depmod in a lifecycle manager that caches builds, tracks versions, and cleans up old artifacts when you purge kernels.

Fedora integrates DKMS with dnf through a systemd service. When dnf install kernel completes, the package manager triggers dkms.service. The service scans /var/lib/dkms/, finds any registered modules that lack a build for the new kernel, and runs the compilation step automatically. You do not need to cron a rebuild script. The package manager handles it.

Convention aside: Fedora ships kernel-devel and kernel-headers as separate packages. DKMS requires kernel-devel to compile against the exact running kernel. Always run sudo dnf install kernel-devel kernel-headers before touching DKMS. Also, dnf upgrade --refresh is your weekly maintenance command. dnf system-upgrade crosses major releases. Keep them separate.

Register the source first. Build second. Install last. The order matters.

Register and build the module

You need a clean source directory and a dkms.conf file before DKMS will accept your module. The configuration file tells DKMS how to compile the code, where to find the Makefile, and where to drop the final .ko file. Without it, DKMS refuses to register the module.

Here is how to install the required build tools and DKMS itself.

sudo dnf install dkms kernel-devel kernel-headers make gcc
# dkms provides the registration and build hooks
# kernel-devel matches the running kernel for header files
# make and gcc are required for the actual compilation step

Place your module source in a persistent location like /opt/mymodule-src or /usr/src/mymodule-1.0.0. DKMS does not copy the source. It points to it and builds in place. Create a dkms.conf file in the root of that directory. The syntax is simple key-value pairs.

Here is a minimal dkms.conf that covers most out-of-tree modules.

# PACKAGE_NAME and PACKAGE_VERSION must match the -m and -v flags exactly
PACKAGE_NAME="mymodule"
PACKAGE_VERSION="1.0.0"

# AUTOREMOVE tells DKMS to clean up old builds when kernels are purged
AUTOREMOVE="yes"

# MAKE tells DKMS how to compile the module relative to the source root
MAKE="make -C /lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules"

# BUILT_MODULE_NAME and DESTINATION dictate where the .ko file lands
BUILT_MODULE_NAME[0]="mymodule"
BUILT_MODULE_LOCATION[0]="build"
DEST_MODULE_LOCATION[0]="/extra"

The MAKE line is the most important field. It tells DKMS to invoke the kernel build system in the correct kernel tree directory. The ${kernelver} and ${dkms_tree} variables are injected automatically at build time. Do not hardcode kernel versions here.

Here is how to register the source, compile it, and install it for the running kernel.

sudo dkms add -m mymodule -v 1.0.0 -s /opt/mymodule-src
# Registers the source path with DKMS without compiling yet
# The -s flag points to the absolute directory containing dkms.conf

sudo dkms build -m mymodule -v 1.0.0
# Compiles the module against the currently running kernel headers
# DKMS caches the build in /var/lib/dkms to speed up future operations

sudo dkms install -m mymodule -v 1.0.0
# Copies the compiled .ko file into /lib/modules/$(uname -r)/extra/
# Runs depmod automatically to update module dependency maps

DKMS stores build logs in /var/lib/dkms/mymodule/1.0.0/build/make.log. If the build fails, read that file before guessing. The compiler output contains the exact missing header or syntax error.

Match the version strings exactly. DKMS treats 1.0 and 1.0.0 as different modules.

Verify the module loaded correctly

A successful install does not guarantee the kernel loaded the module. Some drivers require a specific boot parameter or a udev rule to trigger initialization. Check the DKMS registry first, then verify the kernel state.

Here is how to confirm DKMS tracked the build and the kernel accepted the module.

dkms status
# Lists all registered modules and their build status per kernel
# Look for "installed" next to your current kernel version

lsmod | grep mymodule
# Confirms the kernel actually loaded the module into memory
# An empty output means the module compiled but did not load

If lsmod shows nothing, check the kernel log for initialization failures. The module might have compiled correctly but failed to probe your hardware.

Here is how to read the relevant journal entries.

journalctl -xeu dkms
# Shows DKMS service logs and build output from the last run
# The x flag adds explanatory context to failure lines

journalctl -k | grep mymodule
# Filters kernel ring buffer for module probe messages
# Look for "probe failed" or "no such device" errors

Check dkms status before you assume it works. The kernel does not lie.

Common pitfalls and exact error messages

DKMS fails in predictable ways. Most errors come from mismatched version strings, missing headers, or incorrect dkms.conf paths. Here is what the output looks like and how to fix it.

The dkms add command will refuse to proceed and print Error: Module mymodule/1.0.0 does not exist in /usr/src. This means the -s path is wrong or the directory does not contain a valid dkms.conf. Verify the absolute path and ensure the config file is in the root of the source tree.

If you see make: *** /lib/modules/6.x.x-xx-generic/build: No such file or directory, your kernel-devel package does not match the running kernel. Run sudo dnf install kernel-devel and reboot into the latest kernel. DKMS cannot build against a kernel tree that does not exist.

A build failure often prints ERROR: modpost: "symbol" undefined!. This indicates the module references a kernel function that was removed or renamed in the new kernel version. You must patch your source code to match the current kernel ABI. DKMS cannot fix API changes.

SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. DKMS expects source in /usr/src/ or /opt/. Building in /tmp/ or a user home directory triggers policy blocks. Move the source to a standard location and retry.

Read the actual error before guessing. Half the time the symptom is a missing header package.

When to use DKMS versus other approaches

Choose the right tool based on your update frequency and maintenance appetite.

Use DKMS when you maintain a custom out-of-tree module that must survive kernel updates. Use manual make install when you are testing a driver on a single kernel and do not plan to upgrade. Use akmods when you want Fedora's automated kernel module service to handle rebuilds for you. Use precompiled RPMs from RPM Fusion when the community already maintains a binary package. Stay on upstream DKMS if you need full control over the build flags and source tree layout.

Automate the rebuild. Manual compilation drifts the moment the kernel updates.

Where to go next