Story / scenario opener
You need a kernel feature that Fedora has not backported yet. Or you are debugging a hardware quirk and need to add printk statements to a driver to trace the failure. Or you want to strip out every driver except the ones for your specific laptop to shrink the boot image. Building a custom kernel is the path. It is also the fastest way to brick your boot if you skip a step. This guide walks you through building, installing, and verifying a kernel without losing your ability to log in.
What is actually happening
The kernel is the core of the operating system. Fedora ships a pre-compiled kernel optimized for broad hardware support. When you build from source, you take the raw C code, apply configuration flags, compile it into machine code, and install the resulting binary alongside the existing kernels. GRUB manages the boot menu. If the new kernel fails, GRUB lets you pick the old one.
The risk is not in building. The risk is in overwriting the working kernel or misconfiguring the bootloader so the old entry disappears. Keep the old kernel installed until you verify the new one works. A custom build is an experiment until proven stable.
Install build dependencies
Fedora provides a tool to fetch dependencies automatically. You do not need to guess the package list. The build system requires compilers, libraries, and packaging tools.
sudo dnf install rpm-build fedpkg git # WHY: rpm-build provides rpmbuild and the ~/rpmbuild tree structure. fedpkg helps with Fedora packaging workflows.
sudo dnf download --source kernel # WHY: Downloads the .src.rpm for the current Fedora release kernel.
rpm -i kernel-*.src.rpm # WHY: Installs the source spec and patches into your local build tree.
sudo dnf builddep ~/rpmbuild/SPECS/kernel.spec # WHY: Reads the spec file and installs every library and tool required to compile.
Install dependencies before touching the source. Missing headers cause cryptic compile errors that waste hours.
Get the kernel source
The rpm -i command populates your local build tree. You will find a directory at ~/rpmbuild. The SPECS folder holds the recipe. The SOURCES folder holds the tarballs and patches. The BUILD folder is where compilation happens. Never edit files in SOURCES directly. Edit the spec file or the config.
Respect the directory structure. Drifting files confuse the build system and break reproducibility.
Prepare the build environment
The spec file contains instructions for Fedora's packaging system. You use rpmbuild to run the preparation phase. This unpacks the source and applies all Fedora patches.
cd ~/rpmbuild/SPECS # WHY: Navigate to the directory where the spec file lives.
rpmbuild -bp kernel.spec # WHY: Prepares the build environment. This unpacks the source and applies all Fedora patches.
cd ~/rpmbuild/BUILD/kernel-*/linux-*/ # WHY: Moves into the actual source tree after preparation.
The wildcard in the path handles version numbers. The exact path depends on the kernel version you downloaded. The rpmbuild -bp command stops after preparation. It does not compile. This lets you inspect and modify the source before building.
Configure the kernel
Start from your currently running kernel's configuration. This ensures you keep all drivers that are already working. Copying the config bridges the gap between the running system and the new source.
zcat /proc/config.gz > .config # WHY: Copies the running kernel's configuration. This ensures you keep all currently active drivers.
make olddefconfig # WHY: Updates the config for new options in the source tree. Accepts defaults for anything not in your old config.
Run olddefconfig every time. New kernel versions add options that your old config does not know about. Skipping this step can disable critical drivers or enable incompatible features.
Modify features with menuconfig
The make menuconfig command opens an interactive interface. You can toggle features, enable modules, or search for specific drivers.
make menuconfig # WHY: Opens the interactive configuration interface. Use this to toggle features or search for specific drivers.
Use the / key to search. The interface shows three states for each option. [*] means built-in. The code is compiled directly into the kernel image. [M] means module. The code is compiled as a loadable driver. [ ] means disabled. The code is excluded.
For most hardware, modules are safer. Modules load on demand and can be updated without rebooting. Built-in drivers load immediately. If you disable a driver you need, the system will not boot. If you build a driver in, it consumes memory even if unused.
Search before you guess. The config tree is deep. Use the search function to find the exact option you need.
Compile the kernel
Compilation is CPU-intensive. A modern laptop might finish in ten minutes. A slower machine or a full debug build can take an hour. The build uses all available cores.
make -j$(nproc) # WHY: Compiles the kernel image. Uses all available CPU cores to speed up the process.
make -j$(nproc) modules # WHY: Compiles loadable kernel modules. These are drivers and filesystems loaded on demand.
Watch the output. A silent failure means nothing installed. A red error means you must fix the code or config. Do not force the install. A failed compile usually means a missing dependency or a syntax error in a patch.
Install the kernel and modules
The make install target handles the bootloader update on Fedora. It copies the kernel image, creates the initramfs via dracut, and regenerates the GRUB menu. You rarely need to run grub2-mkconfig manually.
sudo make modules_install # WHY: Copies modules to /lib/modules. This is required for drivers to load.
sudo make install # WHY: Installs the kernel image to /boot and updates the GRUB configuration automatically.
Trust make install. It coordinates the kernel image, initramfs, and bootloader in one atomic step. Manual intervention introduces drift.
Build an RPM for cleaner management
For a cleaner installation, you can build an RPM package. This allows you to manage the kernel with dnf. The package tracks files and supports removal.
rpmbuild -bb kernel.spec # WHY: Builds a binary RPM package. This produces .rpm files in ~/rpmbuild/RPMS/x86_64/.
sudo dnf install ~/rpmbuild/RPMS/x86_64/kernel-*.rpm # WHY: Installs the kernel using the package manager. This tracks files for removal.
Build an RPM for production use. Manual installs leave orphaned files that dnf cannot track.
Verify the installation
Reboot and select the new kernel entry. The GRUB menu lists kernels by version. The new kernel appears at the top. If the system boots, confirm the version.
uname -r # WHY: Prints the version of the currently running kernel.
journalctl -b -1 -p 3 # WHY: Shows errors from the previous boot. Check this if the new kernel failed to start services.
Verify the version. A reboot without checking leaves you guessing which kernel is running.
Common pitfalls and errors
Boot failures happen. The GRUB menu is your safety net. If the new kernel hangs, select the previous kernel entry. Diagnose the failure before attempting a second build.
Check the logs before blaming the kernel. Many boot failures are filesystem or service issues.
Missing modules or initramfs errors
If you see Kernel panic - not syncing: VFS: Unable to mount root fs, the initramfs is missing drivers. The initramfs is a temporary root filesystem. It contains drivers needed to mount the real root. If you build a custom kernel and disable a storage driver, dracut might not include it.
Regenerate the initramfs from the working kernel.
sudo dracut --force # WHY: Regenerates the initramfs. This scans modules and rebuilds the initrd for the running kernel.
SELinux denials
Custom kernels sometimes trigger SELinux relabeling needs. If services fail after boot, check SELinux denials.
journalctl -t setroubleshoot # WHY: Shows SELinux denial summaries. Read these before disabling enforcement.
sudo restorecon -Rv /boot # WHY: Restores file contexts on boot files. Custom kernels sometimes trigger relabeling needs.
Read the denial summary. It tells you exactly which file or service is blocked. Disabling SELinux hides the problem. Fix the context instead.
Upstream kernel builds
If you build from upstream kernel.org source, the steps differ. The upstream make install does not know about Fedora's dracut hooks or GRUB paths. You must regenerate the initramfs and update GRUB manually.
sudo make modules_install # WHY: Installs modules from upstream source.
sudo make install # WHY: Installs the kernel image. Note: This does not update GRUB on Fedora for upstream builds.
sudo dracut --force # WHY: Regenerates the initramfs. Upstream make install skips this step.
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # WHY: Adds the new kernel to the boot menu. Path varies for UEFI systems.
Regenerate the initramfs manually for upstream builds. The upstream make target does not know Fedora's dracut path.
When to use this vs alternatives
Use the Fedora source RPM when you want to modify the kernel config or apply a patch while staying compatible with Fedora updates. Use upstream kernel.org source when you need a feature or bug fix that has not been backported to Fedora yet. Use DKMS when you only need to build a single out-of-tree module like a proprietary GPU driver or a custom network adapter. Stick to the stock kernel when you just want to enable a feature that is already compiled as a module in the Fedora release.