Packaging an out-of-tree module for Debian with DKMS

Vincent Bernat

DKMS is a frame­work de­signed to al­low in­di­vid­ual ker­nel mod­ules to be up­graded with­out chang­ing the whole ker­nel. It is also very easy to re­build mod­ules as you up­grade kernels.

On De­bian-like sys­tems,1 DKMS en­ables the in­stal­la­tion of var­i­ous driv­ers, from ZFS on Linux to VirtualBox ker­nel mod­ules or NVIDIA driv­ers. These out-of-tree mod­ules are not dis­trib­uted as bi­na­ries: once in­stalled, they need to be com­piled for your cur­rent ker­nel. Ev­ery­thing is done au­to­matically:

# apt install zfs-dkms
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  binutils cpp cpp-6 dkms fakeroot gcc gcc-6 gcc-6-base libasan3 libatomic1 libc-dev-bin libc6-dev
  libcc1-0 libcilkrts5 libfakeroot libgcc-6-dev libgcc1 libgomp1 libisl15 libitm1 liblsan0 libmpc3
  libmpfr4 libmpx2 libnvpair1linux libquadmath0 libstdc++6 libtsan0 libubsan0 libuutil1linux libzfs2linux
  libzpool2linux linux-compiler-gcc-6-x86 linux-headers-4.9.0-6-amd64 linux-headers-4.9.0-6-common
  linux-headers-amd64 linux-kbuild-4.9 linux-libc-dev make manpages manpages-dev patch spl spl-dkms
  zfs-zed zfsutils-linux
[…]
3 upgraded, 44 newly installed, 0 to remove and 3 not upgraded.
Need to get 42.1 MB of archives.
After this operation, 187 MB of additional disk space will be used.
Do you want to continue? [Y/n]
[…]
# dkms status
spl, 0.6.5.9, 4.9.0-6-amd64, x86_64: installed
zfs, 0.6.5.9, 4.9.0-6-amd64, x86_64: installed
# modinfo zfs | head
filename:       /lib/modules/4.9.0-6-amd64/updates/dkms/zfs.ko
version:        0.6.5.9-5
license:        CDDL
author:         OpenZFS on Linux
description:    ZFS
srcversion:     42C4AB70887EA26A9970936
depends:        spl,znvpair,zcommon,zunicode,zavl
retpoline:      Y
vermagic:       4.9.0-6-amd64 SMP mod_unload modversions
parm:           zvol_inhibit_dev:Do not create zvol device nodes (uint)

If you in­stall a new ker­nel, a com­pi­la­tion of the mod­ule is au­to­mat­i­cally triggered.

Building your own DKMS-enabled package#

Sup­pose you’ve got­ten your hands on an In­tel XXV710-DA2 NIC. This card is han­dled by the i40e driver. Un­for­tu­nately, it only got sup­port from Linux 4.10 and you are us­ing a stock 4.9 De­bian Stretch ker­nel. DKMS pro­vides here an easy solution!

Down­load the driver from In­tel, un­pack it in some di­rec­tory and add a debian/ sub­di­rec­tory with the fol­low­ing files:

  • debian/​changelog:

    i40e-dkms (2.4.6-0) stretch; urgency=medium
    
      * Initial package.
    
     -- Vincent Bernat <bernat@debian.org>  Tue, 27 Feb 2018 17:20:58 +0100
    
  • debian/​control:

    Source: i40e-dkms
    Maintainer: Vincent Bernat <bernat@debian.org>
    Build-Depends: debhelper (>= 9), dkms
    
    Package: i40e-dkms
    Architecture: all
    Depends: ${misc:Depends}
    Description: DKMS source for the Intel i40e network driver
    
  • debian/rules:

    #!/usr/bin/make -f
    
    include /usr/share/dpkg/pkg-info.mk
    
    %:
            dh $@ --with dkms
    
    override_dh_install:
            dh_install src/* usr/src/i40e-$(DEB_VERSION_UPSTREAM)/
    
    override_dh_dkms:
            dh_dkms -V $(DEB_VERSION_UPSTREAM)
    
    override_dh_auto_configure:
    override_dh_auto_build:
    override_dh_auto_test:
    override_dh_auto_install:
    override_dh_auto_clean:
    
  • debian/​i40e-dkms.dkms:

    PACKAGE_NAME="i40e"
    PACKAGE_VERSION="#MODULE_VERSION#"
    BUILT_MODULE_NAME[0]="$PACKAGE_NAME"
    DEST_MODULE_LOCATION[0]="/updates/dkms"
    AUTOINSTALL="YES"
    REMAKE_INITRD="YES"
    
  • debian/compat:

    9
    

In debian/​changelog, pay at­ten­tion to the ver­sion. The ver­sion of the driver is 2.4.6. There­fore, we use 2.4.6-0 for the pack­age. In debian/​rules, we in­stall the source of the driver in /usr/​src/​i40e-2.4.6—the ver­sion is ex­tracted from debian/​changelog.

The con­tent of debian/​i40e-dkms.​dkms is de­scribed in de­tail in the dkms(8) man­ual page. The i40e driver is fairly stan­dard and dkms is able to fig­ure out how to com­pile it. How­ever, if your ker­nel mod­ule does not fol­low the usual con­ven­tions, it is the right place to over­ride the build command.

Once all the files are in place, you can turn the di­rec­tory into a De­bian pack­age with, for ex­am­ple, the dpkg-buildpackage com­mand.2 At the end of this op­er­a­tion, you get your DKMS-en­abled pack­age, i40e-dkms_​2.4.6-0_​all.​deb. Put it in your in­ter­nal repos­i­tory and in­stall it on the target.

Avoiding compilation on target#

Up­date (2024-03)

DKMS up­stream re­moved sup­port for the dkms mkbmdeb com­mand. This im­pacts ver­sions start­ing from 2.8.8, in­clud­ing the ones shipped in De­bian Book­worm and Ubuntu No­ble. See De­bian bug#1009179 for more in­formation.

Up­date (2026-09)

As a re­place­ment, you can use dkms-mkbmdeb, a stand­alone script with the same fea­tures. Sub­sti­tute dkms mkbmdeb with dkms-mkbmdeb in the ex­am­ple be­low. Thanks to Gavin for this!

If you feel un­com­fort­able in­stalling com­pi­la­tion tools on the tar­get servers, there is a sim­ple so­lu­tion. Since ver­sion 2.2.0.3-5,3 thanks to Thijs Kinkhorst, dkms can build lean bi­nary pack­ages with only the built mod­ules. For each ker­nel ver­sion, you build such a pack­age in your CI system:

KERNEL_VERSION=4.9.0-6-amd64 # could be a Jenkins parameter
apt -qyy install \
      i40e-dkms \
      linux-image-${KERNEL_VERSION} \
      linux-headers-${KERNEL_VERSION}

DRIVER_VERSION=$(dkms status i40e | awk -F', ' '{print $2}')
dkms mkbmdeb i40e/${DRIVER_VERSION} -k ${KERNEL_VERSION}

cd /var/lib/dkms/i40e/${DRIVER_VERSION}/bmdeb/
dpkg -c i40e-modules-${KERNEL_VERSION}_*
dpkg -I i40e-modules-${KERNEL_VERSION}_*

Here is the short­ened out­put of the two last commands:

# dpkg -c i40e-modules-${KERNEL_VERSION}_*
[…]
-rw-r--r-- root/root    551664 2018-03-01 19:16 ./lib/modules/4.9.0-6-amd64/updates/dkms/i40e.ko
[…]
# dpkg -I i40e-modules-${KERNEL_VERSION}_*
 new debian package, version 2.0.
[…]
 Package: i40e-modules-4.9.0-6-amd64
 Source: i40e-dkms-bin
 Version: 2.4.6
 Architecture: amd64
 Installed-Size: 555
 Depends: linux-image-4.9.0-6-amd64
 Provides: i40e-modules
 Section: misc
 Priority: optional
 Description: i40e binary drivers for linux-image-4.9.0-6-amd64
  This package contains i40e drivers for the 4.9.0-6-amd64 Linux kernel,
  built from i40e-dkms for the amd64 architecture.

The gen­er­ated De­bian pack­age con­tains the pre-com­piled driver and only de­pends on the as­so­ci­ated ker­nel. You can safely in­stall it with­out pulling dozens of packages.