diff --git a/debian/control.d/flavour-module.stub b/debian/control.d/flavour-module.stub
new file mode 100644
index 000000000000..2810f83bb361
--- /dev/null
+++ b/debian/control.d/flavour-module.stub
@@ -0,0 +1,19 @@
+
+Package: linux-modules-MODULE-PKGVER-ABINUM-FLAVOUR
+Build-Profiles: <!stage1>
+Architecture: ARCH
+Section: kernel
+Priority: optional
+Depends:
+ ${misc:Depends},
+ linux-image-PKGVER-ABINUM-FLAVOUR | linux-image-unsigned-PKGVER-ABINUM-FLAVOUR,
+Built-Using: ${linux:BuiltUsing}
+Description: Linux kernel MODULE modules for version PKGVER-ABINUM
+ This package provides the Linux kernel MODULE modules for version
+ PKGVER-ABINUM.
+ .
+ You likely do not want to install this package directly. Instead, install the
+ one of the linux-modules-MODULE-FLAVOUR* meta-packages,
+ which will ensure that upgrades work correctly, and that supporting packages are
+ also installed.
+
diff --git a/debian/rules b/debian/rules
index fb2e6af7c537..523f2864765c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -43,6 +43,11 @@ ifneq ($(filter noudeb,$(DEB_BUILD_PROFILES)),)
 disable_d_i=true
 endif
 
+# Default settings for DKMS modules.
+$(foreach _m,$(all_dkms_modules), \
+  $(eval do_$(_m) ?= $(if $(filter $(_m),$(filter-out $(dkms_exclude),$(dkms_include) $(subst any,$(_m),$(subst $(arch),$(_m),$(dkms_$(_m)_archs))))),true,false)) \
+)
+
 # Don't build udebs if $(DEBIAN)/d-i/ doesn't exist
 ifeq ($(wildcard $(DEBIAN)/d-i/.),)
 	disable_d_i=true
@@ -84,8 +89,7 @@ endif
 ifneq ($(filter autopkgtest,$(DEB_BUILD_PROFILES)),)
 	flavours := $(firstword $(flavours))
 	disable_d_i=true
-	do_zfs=false
-	do_v4l2loopback=false
+	$(foreach _m,$(all_dkms_modules),$(eval do_$(_m) = false))
 endif
 
 # Being used to build a mainline build -- turn off things which do not work.
@@ -107,13 +111,11 @@ ifneq ($(do_tools),true)
 	do_tools_host=
 endif
 
-ifeq ($(do_zfs),false)
-	do_zfs_disable:=$(shell for m in $$(cat $(DROOT)/zfs-modules.ignore); do grep -qxF $$m $(prev_abidir)/../modules.ignore 2>/dev/null || echo $$m >> $(prev_abidir)/../modules.ignore; done)
-endif
-
-ifeq ($(do_v4l2loopback),false)
-	do_v4l2loopback_disable:=$(shell for m in $$(cat $(DROOT)/v4l2loopback-modules.ignore); do grep -qxF $$m $(prev_abidir)/../modules.ignore 2>/dev/null || echo $$m >> $(prev_abidir)/../modules.ignore; done)
-endif
+$(foreach _m,$(all_built-in_dkms_modules), \
+  $(if $(filter true,$(do_$(_m))),, \
+    $(eval do_$(_m)_disable := $$(shell for m in $$$$(cat $(DROOT)/$(_m)-modules.ignore); do grep -qxF $$$$m $(prev_abidir)/../modules.ignore 2>/dev/null || echo $$$$m >> $(prev_abidir)/../modules.ignore; done)) \
+  ) \
+)
 
 ifeq ($(do_dkms_wireguard),false)
 	do_wireguard_disable:=$(shell for m in $$(cat $(DROOT)/wireguard-modules.ignore); do grep -qxF $$m $(prev_abidir)/../modules.ignore 2>/dev/null || echo $$m >> $(prev_abidir)/../modules.ignore; done)
@@ -122,10 +124,6 @@ endif
 # Either tools package needs the common source preparation
 do_any_tools=$(sort $(filter-out false,$(do_linux_tools) $(do_cloud_tools)))
 
-# Versions of dkms packages.
-dkms_zfs_linux_version=$(shell gawk '/^zfs-linux / { print $$2; }' debian/dkms-versions)
-dkms_v4l2loopback_version=$(shell gawk '/^v4l2loopback / { print $$2; }' debian/dkms-versions)
-
 # NVIDIA DKMS package gross series split into desktop and server.
 nvidia_desktop_series=$(shell sed -n -e 's/^nvidia-graphics-drivers-\([0-9][0-9]*\) .*/\1/p' debian/dkms-versions)
 nvidia_server_series=$(shell sed -n -e 's/^nvidia-graphics-drivers-\([0-9][0-9]*-server\) .*/\1/p' debian/dkms-versions)
@@ -204,6 +202,7 @@ endif
 $(DEBIAN)/control.stub: 				\
 		$(DROOT)/scripts/control-create		\
 		$(control_files)			\
+		$(DROOT)/control.d/flavour-module.stub	\
 		$(DEBIAN)/changelog			\
 		$(wildcard $(DEBIAN)/control.d/* $(DEBIAN)/sub-flavours/*.vars)
 	for i in $(control_files); do                                           \
diff --git a/debian/rules.d/0-common-vars.mk b/debian/rules.d/0-common-vars.mk
index 3f045aa4d486..e02032cbea6c 100644
--- a/debian/rules.d/0-common-vars.mk
+++ b/debian/rules.d/0-common-vars.mk
@@ -282,3 +282,45 @@ custom_override = \
 
 # selftests that Ubuntu cares about
 ubuntu_selftests = breakpoints cpu-hotplug efivarfs memfd memory-hotplug mount net ptrace seccomp timers powerpc user ftrace
+
+# DKMS
+all_dkms_modules =
+$(foreach _line,$(shell gawk '{ OFS = "!"; $$1 = $$1; print }' $(DROOT)/dkms-versions), \
+  $(eval _params = $(subst !, ,$(_line))) \
+  $(eval _deb_pkgname = $(firstword $(_params))) \
+  $(eval _deb_version = $(word 2,$(_params))) \
+  $(if $(filter modulename=%,$(_params)), \
+    $(eval _m = $(word 2,$(subst =, ,$(filter modulename=%,$(_params))))) \
+    , \
+    $(info modulename for $(_deb_pkgname) not specified in dkms-versions. Assume $(_deb_pkgname).) \
+    $(eval _m = $(_deb_pkgname)) \
+  ) \
+  $(eval all_dkms_modules += $(_m)) \
+  $(eval dkms_$(_m)_version = $(_deb_version)) \
+  $(if $(filter debpath=%,$(_params)), \
+    $(eval dkms_$(_m)_debpath = $(strip \
+      $(subst %package%,$(_deb_pkgname), \
+        $(subst %version%,$(lastword $(subst :, ,$(_deb_version))), \
+          $(subst %module%,$(_m),$(patsubst debpath=%,%,$(filter debpath=%,$(_params)))) \
+        ) \
+      ) \
+    )) \
+    , \
+    $(error debpath for $(_deb_pkgname) not specified.) \
+  ) \
+  $(if $(filter arch=%,$(_params)), \
+    $(eval dkms_$(_m)_archs = $(patsubst arch=%,%,$(filter arch=%,$(_params)))) \
+    , \
+    $(eval dkms_$(_m)_archs = any) \
+  ) \
+  $(eval dkms_$(_m)_rprovides = $(patsubst rprovides=%,%,$(filter rprovides=%,$(_params)))) \
+  $(eval dkms_$(_m)_type = $(word 1,$(patsubst type=%,%,$(filter type=%,$(_params))) built-in)) \
+  $(eval all_$(dkms_$(_m)_type)_dkms_modules += $(_m)) \
+  $(if $(filter standalone,$(dkms_$(_m)_type)), \
+    $(eval dkms_$(_m)_pkg_name = linux-modules-$(_m)-$(abi_release)) \
+    $(eval dkms_$(_m)_subdir = ubuntu) \
+    , \
+    $(eval dkms_$(_m)_pkg_name = $(mods_pkg_name)) \
+    $(eval dkms_$(_m)_subdir = kernel) \
+  ) \
+)
diff --git a/debian/rules.d/2-binary-arch.mk b/debian/rules.d/2-binary-arch.mk
index 0cc5687052f4..270c78185b13 100644
--- a/debian/rules.d/2-binary-arch.mk
+++ b/debian/rules.d/2-binary-arch.mk
@@ -117,8 +117,10 @@ $(stampdir)/stamp-install-%: MODSECKEY=$(builddir)/build-$*/certs/signing_key.pe
 $(stampdir)/stamp-install-%: MODPUBKEY=$(builddir)/build-$*/certs/signing_key.x509
 $(stampdir)/stamp-install-%: build_dir=$(builddir)/build-$*
 $(stampdir)/stamp-install-%: dkms_dir=$(call dkms_dir_prefix,$(builddir)/build-$*)
-$(stampdir)/stamp-install-%: enable_zfs = $(call custom_override,do_zfs,$*)
-$(stampdir)/stamp-install-%: enable_v4l2loopback = $(call custom_override,do_v4l2loopback,$*)
+$(foreach _m,$(all_dkms_modules), \
+  $(eval $$(stampdir)/stamp-install-%: enable_$(_m) = $$(filter true,$$(call custom_override,do_$(_m),$$*))) \
+  $(eval $$(stampdir)/stamp-install-%: dkms_$(_m)_pkgdir = $$(CURDIR)/debian/$(dkms_$(_m)_pkg_name)-$$*) \
+)
 $(stampdir)/stamp-install-%: dbgpkgdir_dkms = $(if $(filter true,$(skipdbg)),"",$(dbgpkgdir)/usr/lib/debug/lib/modules/$(abi_release)-$*/kernel)
 $(stampdir)/stamp-install-%: $(stampdir)/stamp-build-% $(stampdir)/stamp-install-headers
 	@echo Debug: $@ kernel_file $(kernel_file) kernfile $(kernfile) install_file $(install_file) instfile $(instfile)
@@ -126,6 +128,9 @@ $(stampdir)/stamp-install-%: $(stampdir)/stamp-build-% $(stampdir)/stamp-install
 	dh_prep -p$(bin_pkg_name)-$*
 	dh_prep -p$(mods_pkg_name)-$*
 	dh_prep -p$(hdrs_pkg_name)-$*
+	$(foreach _m,$(all_standalone_dkms_modules), \
+	  $(if $(enable_$(_m)),dh_prep -p$(dkms_$(_m)_pkg_name)-$*;)\
+	)
 ifneq ($(skipdbg),true)
 	dh_prep -p$(bin_pkg_name)-$*-dbgsym
 endif
@@ -269,6 +274,12 @@ ifeq ($(do_extras_package),true)
 		$(call install_control,$(mods_extra_pkg_name)-$*,extra,postinst postrm); \
 	fi
 endif
+	$(foreach _m,$(all_standalone_dkms_modules), \
+	  $(if $(enable_$(_m)), \
+	    install -d $(dkms_$(_m)_pkgdir)/usr/lib/linux/triggers; \
+	    $(call install_control,$(dkms_$(_m)_pkg_name)-$*,extra,postinst postrm); \
+	  ) \
+	)
 
 	# Install the full changelog.
 ifeq ($(do_doc_package),true)
@@ -418,8 +429,11 @@ endif
 	install -d $(dkms_dir) $(dkms_dir)/headers $(dkms_dir)/build $(dkms_dir)/source
 	cp -rp "$(hdrdir)" "$(indep_hdrdir)" "$(dkms_dir)/headers"
 
-	$(if $(filter true,$(enable_zfs)),$(call build_dkms, $(mods_pkg_name)-$*, $(pkgdir)/lib/modules/$(abi_release)-$*/kernel, $(dbgpkgdir_dkms), zfs, pool/universe/z/zfs-linux/zfs-dkms_$(dkms_zfs_linux_version)_all.deb))
-	$(if $(filter true,$(enable_v4l2loopback)),$(call build_dkms, $(mods_pkg_name)-$*, $(pkgdir)/lib/modules/$(abi_release)-$*/kernel, $(dbgpkgdir_dkms), v4l2loopback, pool/universe/v/v4l2loopback/v4l2loopback-dkms_$(dkms_v4l2loopback_version)_all.deb))
+	$(foreach _m,$(all_dkms_modules), \
+	  $(if $(enable_$(_m)), \
+	    $(call build_dkms,$(dkms_$(_m)_pkg_name)-$*,$(dkms_$(_m)_pkgdir)/lib/modules/$(abi_release)-$*/$(dkms_$(_m)_subdir),$(dbgpkgdir_dkms),$(_m),$(dkms_$(_m)_debpath)); \
+	  ) \
+	)
 
 
 ifneq ($(skipdbg),true)
@@ -548,7 +562,7 @@ define dh_all
 	dh_shlibdeps -p$(1) $(shlibdeps_opts)
 	dh_installdeb -p$(1)
 	dh_installdebconf -p$(1)
-	$(lockme) dh_gencontrol -p$(1) -- -Vlinux:rprovides='$(rprovides)'
+	$(lockme) dh_gencontrol -p$(1) -- -Vlinux:rprovides='$(rprovides)' $(2)
 	dh_md5sums -p$(1)
 	dh_builddeb -p$(1)
 endef
@@ -557,7 +571,7 @@ define newline
 
 endef
 define dh_all_inline
-        $(subst ${newline},; \${newline},$(call dh_all,$(1)))
+        $(subst ${newline},; \${newline},$(call dh_all,$(1),$(2)))
 endef
 
 binary-arch-headers: install-arch-headers
@@ -582,8 +596,10 @@ binary-%: dbgpkg = $(bin_pkg_name)-$*-dbgsym
 binary-%: dbgpkgdir = $(CURDIR)/debian/$(bin_pkg_name)-$*-dbgsym
 binary-%: pkgtools = $(tools_flavour_pkg_name)-$*
 binary-%: pkgcloud = $(cloud_flavour_pkg_name)-$*
-binary-%: rprovides = $(if $(filter true,$(call custom_override,do_zfs,$*)),spl-modules$(comma) spl-dkms$(comma) zfs-modules$(comma) zfs-dkms$(comma)) \
-		$(if $(filter true,$(call custom_override,do_v4l2loopback,$*)),v4l2loopback-modules$(comma) v4l2loopback-dkms$(comma))
+$(foreach _m,$(all_dkms_modules), \
+  $(eval binary-%: enable_$(_m) = $$(filter true,$$(call custom_override,do_$(_m),$$*))) \
+)
+binary-%: rprovides = $(foreach _m,$(all_built-in_dkms_modules),$(if $(enable_$(_m)),$(foreach _r,$(dkms_$(_m)_rprovides),$(_r)$(comma) )))
 binary-%: target_flavour = $*
 binary-%: checks-%
 	@echo Debug: $@
@@ -608,6 +624,10 @@ ifeq ($(do_extras_package),true)
   endif
 endif
 
+	$(foreach _m,$(all_standalone_dkms_modules), \
+	  $(if $(enable_$(_m)),$(call dh_all,$(dkms_$(_m)_pkg_name)-$*);)\
+	)
+
 	$(call dh_all,$(pkgbldinfo))
 	$(call dh_all,$(pkghdr))
 
diff --git a/debian/scripts/control-create b/debian/scripts/control-create
index 2ab9e497515f..5d0e3e46556c 100755
--- a/debian/scripts/control-create
+++ b/debian/scripts/control-create
@@ -38,3 +38,30 @@ cat $stub | grep -v '^#' | sed \
 	-e "s#=SIGN-ME-PKG=#$sign_me_pkg#g"	\
 	-e "s#=SIGN-ME-TXT=#$sign_me_txt#g"	\
 	-e "s#=SIGN-PEER-PKG=#$sign_peer_pkg#g"
+
+while read package version extras
+do
+	module="$package"
+	module_type=
+
+	# Module arch parameters are skipped here, so a package section will
+	# be generated for each flavour, and its Architecture will be set to
+	# all architectures with that flavour. Even that is being generated,
+	# it doesn't follow all of them will be built. That's to work-around
+	# dkms_exclude/dkms_include that manipulates supported architectures
+	# in $(DEBIAN)/rules.d/$(arch).mk.
+	for param in $extras; do
+		case "$param" in
+		modulename=*) module="${param#modulename=}" ;;
+		type=*) module_type="${param#type=}" ;;
+		*) continue ;;
+		esac
+	done
+
+	[ "$module_type" = "standalone" ] || continue
+
+	cat debian/control.d/flavour-module.stub | grep -v '^#' | sed	\
+		-e "s#ARCH#$arch#g"		\
+		-e "s#MODULE#$module#g"		\
+		-e "s#FLAVOUR#$flavour#g"
+done <"debian/dkms-versions"
