Build a Custom Kernel the Debian Way
Build a custom Linux kernel on Debian or Ubuntu using make deb-pkg, producing clean .deb packages for easy install, update, and rollback.
Before you start
- ▸Debian, Ubuntu, or derivative with sudo access
- ▸At least 15 GB free disk space in the build directory
- ▸4 GB RAM or equivalent swap space
- ▸Basic familiarity with kernel config symbols and the GRUB bootloader
Building a custom kernel on Debian gives you fine-grained control over hardware support, scheduler tuning, security hardening flags, and out-of-tree module compatibility. Debian's toolchain wraps the kernel build in proper .deb packages, so you get clean installs, easy rollbacks, and DKMS compatibility without touching files directly under /lib/modules by hand. This guide uses the make deb-pkg method, which is Debian's current recommended approach — make-kpkg (from the kernel-package tool) is legacy and largely unmaintained since Debian Stretch. We cover both approaches so you understand what you may find in older documentation, but the primary path here is make deb-pkg.
Prerequisites and Package Setup
You need a Debian or Ubuntu host with at least 15 GB of free space on the build partition and roughly 4 GB of RAM (or a swap file of equivalent size) for link-time optimization passes. An internet connection is required to pull source and dependencies.
Install Build Dependencies
sudo apt update
sudo apt install -y build-essential libncurses-dev bison flex libssl-dev \
libelf-dev dwarves bc rsync cpio pahole fakeroot dpkg-dev \
linux-source
pahole is required for BTF (BPF Type Format) generation in kernels 5.2+. Omitting it causes a silent build failure on modern trees. dwarves provides pahole on most releases; on Ubuntu 20.04 you may need a backport or PPA.
Locate the Source
The linux-source package drops a compressed tarball in /usr/src/. Unpack it into your home directory to avoid working as root during compilation.
ls /usr/src/linux-source-*.tar.xz
cd ~
tar -xf /usr/src/linux-source-6.1.tar.xz # adjust version to match
cd linux-source-6.1
Configuring the Kernel
The config step is where you customise what gets compiled in, compiled as a module, or excluded entirely. Getting a usable config from scratch takes experience; starting from a known-good base is almost always the right call.
Option A — Base Your Config on the Running Kernel
cp /boot/config-$(uname -r) .config
make olddefconfig
olddefconfig silently accepts all new symbols at their defaults. Use oldconfig instead if you want to answer each new symbol interactively. This approach means your custom kernel will support all the hardware your current kernel supports, which is the safest starting point.
Option B — Interactive Configuration
make menuconfig
Navigate with arrow keys; press Space to toggle [*] (built-in), [M] (module), or [ ] (excluded). Press / to search by symbol name. Save and exit when done.
Reducing Build Time with localmodconfig
On a development or desktop machine, trim the config to only modules currently loaded. This can cut compile time from 90 minutes to under 20 on a modern CPU.
lsmod > /tmp/lsmod.txt
make LSMOD=/tmp/lsmod.txt localmodconfig
Warning: Do not use localmodconfig on a server or any machine where you may swap hardware or boot different configurations — you will be missing drivers you need.
Disable the Trusted Key Requirement (Important on Debian/Ubuntu)
Debian's default config requires a signing key that your build environment won't have. Without this change, the build aborts late in the process.
scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS
Building the Kernel Packages
The make deb-pkg target produces installable .deb files: a kernel image package, a headers package, a libc-dev package, and optionally a debug package. All output lands in the parent directory (~/ in this walkthrough).
Set a Local Version String
Tagging your build prevents it from colliding with distribution-provided kernels and makes it easy to identify in GRUB and uname -r.
scripts/config --set-str LOCALVERSION "-custom"
Compile and Package
Replace -j$(nproc) with a lower thread count if the machine is shared or low on RAM. The KDEB_PKGVERSION variable sets the Debian package version string.
make -j$(nproc) deb-pkg LOCALVERSION=-custom KDEB_PKGVERSION=1.0
Expect output like dpkg-deb: building package 'linux-image-6.1.0-custom' near the end. Build time varies from 15 minutes on a 16-core workstation to over two hours on a 2-core VM.
Legacy Reference: make-kpkg (Informational Only)
Older Debian guides use fakeroot make-kpkg from the kernel-package utility. The equivalent command looked like:
# LEGACY — do not use on modern Debian; shown for reference only
fakeroot make-kpkg --initrd --revision=1.0 kernel_image kernel_headers
kernel-package is still in the Debian archive but receives no upstream fixes and does not correctly handle BTF, zstd compression, or modern config symbols. Use make deb-pkg instead.
Installing the Packages
Locate and Install the .deb Files
ls ../*.deb
You will see files similar to (output varies by version):
# ../linux-image-6.1.0-custom_1.0_amd64.deb
# ../linux-headers-6.1.0-custom_1.0_amd64.deb
# ../linux-libc-dev_1.0_amd64.deb
sudo dpkg -i ../linux-image-6.1.0-custom_1.0_amd64.deb \
../linux-headers-6.1.0-custom_1.0_amd64.deb
dpkg automatically runs update-initramfs and update-grub via the package's post-install scripts, so the new kernel will appear in GRUB on next boot without manual intervention.
Verify GRUB Entry
grep -i custom /boot/grub/grub.cfg | grep menuentry
If you see your version string, GRUB is ready. Reboot and select the new entry from the GRUB menu (hold Shift at POST on BIOS systems, or Esc on UEFI to force the menu if GRUB_TIMEOUT is 0).
Post-Boot Verification
uname -r
Expect output like 6.1.0-custom. Then confirm modules load correctly:
lsmod | head -20
dmesg | grep -i error | head -20
Check that your primary storage, network, and GPU drivers are present. If you used localmodconfig, pay special attention to any hardware that wasn't active during the module capture step.
Rolling Back
Because you installed a .deb, removal is clean:
sudo apt remove linux-image-6.1.0-custom
This triggers update-grub automatically and removes the GRUB entry. Reboot into your previous kernel from the GRUB menu before running the remove command if you are already running the custom kernel.
Troubleshooting
Build Fails: "No rule to make target 'debian/certs/..."
You forgot to disable the trusted key symbols. Run the scripts/config commands in the configuration section and then re-run make deb-pkg.
initramfs Missing / Kernel Panics on Boot
Ensure CONFIG_BLK_DEV_INITRD is enabled and your root filesystem driver (ext4, xfs, btrfs, etc.) is compiled in — not as a module — if you are not using an initrd. On Debian, always build with --initrd semantics; the post-install hook handles this when installing via dpkg.
DKMS Modules Fail to Build Against the New Kernel
Install the headers package (linux-headers-6.1.0-custom) before triggering DKMS. Then rebuild explicitly:
sudo dkms autoinstall -k 6.1.0-custom
Build OOMs on Low-RAM Systems
Reduce parallelism and add swap if needed:
sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
make -j2 deb-pkg LOCALVERSION=-custom KDEB_PKGVERSION=1.0Frequently asked questions
- Why use make deb-pkg instead of the older make-kpkg / fakeroot approach?
- make-kpkg (kernel-package) is unmaintained and does not handle BTF, zstd compression, or many modern config symbols correctly. make deb-pkg is maintained in the upstream kernel tree and produces equivalent packages with none of those limitations.
- Can I build a Debian kernel package on Ubuntu or Linux Mint?
- Yes. The make deb-pkg method works on any Debian-derived system. Install the same build dependencies via apt; the resulting .deb can be installed on the build host or transferred to another Debian/Ubuntu machine of the same architecture.
- How do I enable or disable a specific kernel feature like PREEMPT_RT?
- Run make menuconfig and search for the symbol with /. For PREEMPT_RT you need either a kernel version that includes the RT patches (6.6+ has partial mainline support) or to apply the rt-patch series manually before running menuconfig.
- Will DKMS modules like NVIDIA or VirtualBox work with my custom kernel?
- Yes, provided you install the linux-headers package for your custom kernel. DKMS uses the headers to compile modules at install time; run dkms autoinstall -k <version> after installing the headers package.
- How much disk space does the full build require?
- Expect 10-15 GB for the source tree, build artifacts, and final .deb files combined. Using localmodconfig can reduce the object file footprint significantly but does not reduce the source tree size.
Related guides
AI and Artificial-Life Tools on Linux
Set up open-source AI/ML and artificial-life toolkits on Linux: PyTorch, JAX, DEAP, Avida, NetLogo, and RL environments with GPU driver guidance.
Assembly Language on Linux: A Starter Guide
Write x86-64 assembly on Linux from scratch: install NASM and GAS, learn syscalls, assemble and link a working program, then inspect and debug it.
How to Benchmark Disk Performance with fio
Learn to benchmark Linux disk performance with fio: writing job files, testing latency and throughput, and interpreting IOPS and percentile output correctly.
The Linux Boot Process Explained
Trace the full Linux boot sequence from UEFI firmware through GRUB2, the kernel, initramfs, and systemd to your login prompt — with diagnostics at each stage.