diff --git a/meta/recipes-kernel/linux/kernel-devsrc.bb b/meta/recipes-kernel/linux/kernel-devsrc.bb
index 90bdbfc534..3b83fd145c 100644
--- a/meta/recipes-kernel/linux/kernel-devsrc.bb
+++ b/meta/recipes-kernel/linux/kernel-devsrc.bb
@@ -28,30 +28,31 @@ B = "${STAGING_KERNEL_BUILDDIR}"
 
 PACKAGE_ARCH = "${MACHINE_ARCH}"
 
+# The flags choose what goes in the kernel-devsrc tree:
+#   build:  what is needed to build out of tree modules against the kernel
+#           ('make scripts prepare' on target). The default, and what
+#           kernel-devsrc has always been.
+#   source: the source needed to debug a kernel built for a specific
+#           MACHINE, see KERNELDEV_SOURCE_SPINE. No runtime dependencies,
+#           so it can go in a debug rootfs without the build tools.
+PACKAGECONFIG ?= "build"
+PACKAGECONFIG[build] = ",,,${KERNELDEV_BUILD_RDEPENDS}"
+PACKAGECONFIG[source] = ",,"
+
+# The curated part of the 'source' level, copied in full along with
+# arch/${ARCH}. The rest (drivers, sound, samples, ...) is not curated: the
+# files the kernel build read are discovered from the manifest the kernel
+# stages in do_shared_workdir. The manifest could find the spine's files too,
+# but the spine is listed so that 'source' always has a sane default.
+KERNELDEV_SOURCE_SPINE ?= "block certs crypto fs include init io_uring ipc kernel lib mm net security virt"
+
 KERNEL_BUILD_ROOT = "${nonarch_base_libdir}/modules/"
 
-do_install() {
+# The 'build' level: what is needed to build out of tree modules against the
+# kernel ('make scripts prepare' on target), and what kernel-devsrc has
+# always contained.
+kernel_devsrc_install_build() {
     kerneldir=${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}
-    install -d $kerneldir
-
-    # create the directory structure
-    rm -f $kerneldir/build
-    rm -f $kerneldir/source
-    mkdir -p $kerneldir/build
-
-    # for compatibility with some older variants of this package, we
-    # create  a /usr/src/kernel symlink to /lib/modules/<version>/source
-    mkdir -p ${D}/usr/src
-    (
-        cd ${D}/usr/src
-        ln -rs ${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}/source kernel
-    )
-
-    # for on target purposes, we unify build and source
-    (
-        cd $kerneldir
-        ln -s build source
-    )
 
     # first copy everything
     (
@@ -448,7 +449,58 @@ do_install() {
         sed -i 's,/usr/bin/env python2,/usr/bin/env python3,' "$ss"
         sed -i 's,/usr/bin/python$,/usr/bin/env python3,' "$ss"
     done
+}
+
+# The 'source' level: the curated spine plus the files the kernel build read,
+# from the manifest the kernel stages in do_shared_workdir. Only source files
+# are copied, never scripts, so the package gets no interpreter dependencies.
+kernel_devsrc_install_source() {
+    kerneldir=${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}
 
+    if [ ! -s ${B}/kernel-source-files ]; then
+        bbfatal "${B}/kernel-source-files is missing, the kernel did not stage its source manifest"
+    fi
+    (
+        cd ${S}
+        {
+            find ${KERNELDEV_SOURCE_SPINE} arch/${ARCH} -type f
+            cat ${B}/kernel-source-files
+        } | grep -E '\.([chS]|rs)$' | sort -u | cpio --quiet -pdlu $kerneldir/build
+    )
+}
+
+do_install() {
+    kerneldir=${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}
+    install -d $kerneldir
+
+    # create the directory structure
+    rm -f $kerneldir/build
+    rm -f $kerneldir/source
+    mkdir -p $kerneldir/build
+
+    # for compatibility with some older variants of this package, we
+    # create  a /usr/src/kernel symlink to /lib/modules/<version>/source
+    mkdir -p ${D}/usr/src
+    (
+        cd ${D}/usr/src
+        ln -rs ${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}/source kernel
+    )
+
+    # for on target purposes, we unify build and source
+    (
+        cd $kerneldir
+        ln -s build source
+    )
+
+    if [ -n "${@bb.utils.contains('PACKAGECONFIG', 'build', '1', '', d)}" ]; then
+        kernel_devsrc_install_build
+    fi
+
+    # The 'source' level goes in the same tree, at /usr/src/kernel, which is
+    # where the kernel's debug info points.
+    if [ -n "${@bb.utils.contains('PACKAGECONFIG', 'source', '1', '', d)}" ]; then
+        kernel_devsrc_install_source
+    fi
     chown -R root:root ${D}
 }
 
@@ -457,14 +509,15 @@ do_install[lockfiles] = "${TMPDIR}/kernel-scripts.lock"
 
 FILES:${PN} = "${KERNEL_BUILD_ROOT} ${KERNEL_SRC_PATH}"
 
-RDEPENDS:${PN} = "bc python3-core flex bison ${TCLIBC}-utils gawk"
+# Added to RDEPENDS:${PN} by the 'build' PACKAGECONFIG
+KERNELDEV_BUILD_RDEPENDS = "bc python3-core flex bison ${TCLIBC}-utils gawk"
 # 4.15+ needs these next two RDEPENDS
-RDEPENDS:${PN} += "openssl-dev util-linux"
+KERNELDEV_BUILD_RDEPENDS += "openssl-dev util-linux"
 # and x86 needs a bit more for 4.15+
-RDEPENDS:${PN} += "${@bb.utils.contains('ARCH', 'x86', 'elfutils-dev', '', d)}"
+KERNELDEV_BUILD_RDEPENDS += "${@bb.utils.contains('ARCH', 'x86', 'elfutils-dev', '', d)}"
 # powerpc needs elfutils on 6.3+
-RDEPENDS:${PN} += "${@bb.utils.contains('ARCH', 'powerpc', 'elfutils-dev', '', d)}"
+KERNELDEV_BUILD_RDEPENDS += "${@bb.utils.contains('ARCH', 'powerpc', 'elfutils-dev', '', d)}"
 # 5.8+ needs gcc-plugins libmpc-dev
-RDEPENDS:${PN} += "gcc-plugins libmpc-dev"
+KERNELDEV_BUILD_RDEPENDS += "gcc-plugins libmpc-dev"
 # 5.13+ needs grep for powerpc
-RDEPENDS:${PN}:append:powerpc = " grep"
+KERNELDEV_BUILD_RDEPENDS:append:powerpc = " grep"
