Building a Custom FreeBSD Kernel from Source
Why and how to build a custom FreeBSD kernel configuration, from copying GENERIC to installing the result.
Most FreeBSD installations never need anything beyond the stock GENERIC kernel — it’s built to run on the widest possible range of hardware, at the cost of including drivers and options you’ll never use. Building a custom kernel is still a well-trodden path for specific needs: stripping unused drivers for a faster boot and smaller memory footprint, enabling debugging options for kernel development, or applying options that can only be set at compile time.
Getting the source
Kernel builds require the FreeBSD source tree, which — unlike the ports tree — isn’t present by default on most installations:
git clone https://git.FreeBSD.org/src.git /usr/src
cd /usr/src
git checkout releng/14.2
Starting from GENERIC
Kernel configurations live in /usr/src/sys/<arch>/conf/, and every custom configuration starts life as a copy of GENERIC, edited rather than written from scratch:
cd /usr/src/sys/amd64/conf
cp GENERIC MYKERNEL
A configuration file is a flat list of options, device, and machine directives:
# MYKERNEL
include GENERIC
ident MYKERNEL
# Enable resource limit accounting
options RACCT
options RCTL
# Drop drivers this hardware will never need
nodevice sound
nodevice firewire
# Kernel debugging
options KDB
options DDB
options WITNESS
The include GENERIC line means the config is expressed as a diff against GENERIC — additions and, via nodevice/nooptions, explicit removals — rather than a full re-specification of every driver, which is both shorter and safer against accidentally leaving out something essential like the driver for your boot disk.
Building
The buildkernel and installkernel targets in /usr/src’s top-level Makefile drive the whole process:
cd /usr/src
make -j$(sysctl -n hw.ncpu) buildkernel KERNCONF=MYKERNEL
make installkernel KERNCONF=MYKERNEL
buildkernel compiles the kernel and its modules into /usr/obj/usr/src/<arch>/sys/MYKERNEL; installkernel copies the result into /boot/kernel, first moving the previous kernel to /boot/kernel.old as an automatic rollback point.
reboot
If the new kernel fails to boot, selecting kernel.old from the loader menu (or boot kernel.old at the loader prompt) boots the previous, known-good kernel without needing any recovery media.
Building world alongside the kernel
A custom kernel config alone doesn’t change the userland — but kernel and userland are expected to stay closely version-matched on FreeBSD, especially across anything beyond a minor patch release. The full, recommended update sequence rebuilds both:
make -j$(sysctl -n hw.ncpu) buildworld
make -j$(sysctl -n hw.ncpu) buildkernel KERNCONF=MYKERNEL
make installkernel KERNCONF=MYKERNEL
reboot # into single-user mode for the world install
make installworld
make delete-old delete-old-libs
mergemaster -Ui
mergemaster reconciles /etc configuration files against the templates shipped by the new userland — a step that’s easy to skip and later regret when a config file format has changed underneath an un-migrated /etc file.
Tuning without a rebuild: loader.conf and sysctl
Not everything requires a custom kernel. Many kernel parameters are tunable at boot time via /boot/loader.conf, or even at runtime via sysctl, without touching the kernel configuration at all:
# /boot/loader.conf
kern.maxfiles="65536"
sysctl kern.ipc.somaxconn=4096
A custom kernel build is really only necessary for things that must be decided at compile time — adding or removing a driver, enabling WITNESS/INVARIANTS for kernel debugging, or a handful of options with no runtime-tunable equivalent. Reaching for loader.conf or sysctl.conf first, and a custom kernel only when a specific option genuinely requires it, keeps the system on well-tested GENERIC code as much as possible.