Skip to content
daniel@cosenza:~/blog
FreeBSDDeep Dive June 22, 2026 3 min read

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.