For the complete documentation index, see llms.txt. This page is also available as Markdown.

40.6 Building a Custom Kernel

The kernel is the core of the FreeBSD operating system, responsible for managing memory, enforcing security controls, network communication, disk access, and other tasks. Although most of FreeBSD is dynamically configurable, some users may wish to configure and compile a custom kernel.

Why Build a Custom Kernel

Traditionally, FreeBSD uses a monolithic kernel. A monolithic kernel is a large program that supports a fixed list of devices; changing kernel behavior requires recompilation and rebooting into the new kernel.

Currently, most functionality in the FreeBSD kernel is contained in modules that can be dynamically loaded and unloaded as needed. This allows the running kernel to adapt to new hardware without rebooting, hence the term modular kernel.

Sometimes, static kernel configuration is still necessary. Some features are tightly bound to the kernel and cannot be dynamically loaded. Additionally, some security environments prevent loading and unloading of kernel modules, requiring all needed functionality to be statically compiled into the kernel.

Although the process of building a custom kernel is time-consuming, it has practical value. Unlike the default GENERIC kernel (which must support a wide range of hardware), a custom kernel can be streamlined to support only the local hardware. The main advantages of a custom kernel are:

  • Reduced boot time. A custom kernel only probes local hardware, reducing boot time.

  • Reduced memory usage. A custom kernel reduces memory usage by omitting unused features and device drivers. Kernel code always resides in physical memory and applications cannot use this memory, so custom kernels are especially important for systems with limited memory.

  • Enhanced hardware support. A custom kernel can provide support for devices not present in the GENERIC kernel.

Warning

Non-default configurations are not tested as thoroughly as the GENERIC configuration. While custom kernels can provide specific benefits, they also increase the risk of encountering build or runtime issues. Custom kernel configurations are only recommended for professional users who have clear reasons for changes and are willing to debug when necessary.

Before building a custom kernel, its purpose should be clear. Specifically, if specific hardware support is needed, that support may already exist as a module; try loading the relevant module with kldload(8) first.

Kernel modules are located in /boot/kernel and can be dynamically loaded into the running kernel using kldload(8).

An example of the contents of the /boot/kernel directory is as follows:

ykla@ykla:~ $ ls /boot/kernel
aac.ko				iwn105fw.ko
ahci.ko				lindebugfs.ko

...output omitted...

u3g.ko				xdr.ko
iwi_ibss.ko			xhci.ko
iwi_monitor.ko			xz.ko
zfs.ko				zlib.ko

Most kernel drivers have loadable modules.

If you need to load the u3g driver as a module at boot time, add the following line to the /boot/loader.conf file:

The system will dynamically load this module at boot time.

However, in some cases, there is no associated module in /boot/kernel, which typically involves specific subsystems.

Preparation

Obtaining Kernel Source Code

The FreeBSD operating system is developed and maintained as a whole, so both the kernel and user space are located in the freebsd-src project.

To create a custom kernel configuration file and build a custom kernel, you must first install the complete FreeBSD source tree.

If the /usr/src/ directory does not exist or is empty, the source code has not been installed. You can obtain the source code via Git or download the corresponding src archive from a mirror site.

Example: Obtain FreeBSD source code via the 16.0-CURRENT archive and extract it to the /usr/src/ directory.

The above example obtains the source code for -CURRENT (development branch). For -STABLE (stable branch) and -RELEASE (release), the archive download paths are different:

  • -STABLE: https://download.freebsd.org/snapshots/amd64/X.Y-STABLE/src.txz (e.g., 15.1-STABLE, 14.4-STABLE)

  • -RELEASE: https://download.freebsd.org/releases/amd64/X.Y-RELEASE/src.txz (e.g., 15.0-RELEASE, 14.4-RELEASE)

You can also obtain the source code for a specific branch via Git:

Where -b specifies the branch name: main corresponds to -CURRENT, stable/X corresponds to -STABLE, and releng/X.Y corresponds to -RELEASE.

The kernel source code path is /usr/src/sys.

Creating a Kernel Configuration File

File Naming Conventions

After installation, view the contents of the /usr/src/sys directory:

This directory contains multiple subdirectories, including directories representing supported architectures (such as amd64, i386, arm, riscv, powerpc, etc.).

Each architecture-specific directory only handles content related to that architecture; other code is machine-independent code common to all platforms.

Each supported architecture has a conf subdirectory containing the GENERIC kernel configuration file for that architecture.

Therefore, instead of editing the GENERIC file directly, you should copy the file and edit the copy. It is recommended that configuration file names be all uppercase. When managing multiple FreeBSD hosts with different hardware, it is recommended to name configuration files after the hostname.

Note

When building the kernel, the system creates a directory with the same name as the configuration under /usr/obj/usr/src/amd64.amd64/sys/ to store build artifacts.

Editing the Configuration File

The following example creates a copy named MYKERNEL of the GENERIC configuration file for a 16.0-CURRENT system on the amd64 architecture:

You can then customize the kernel configuration file MYKERNEL using a text editor.

The format of the kernel configuration file is concise.

Warning

Removing support for devices or options may corrupt the kernel. For example, if the NVMe driver is removed from the kernel configuration file, systems using the nvme disk driver may fail to boot. If in doubt, it is recommended to keep the support in the kernel.

The include directive can be used in the configuration file to include another configuration file into the current one, making it easy to maintain minor changes on top of an existing configuration. If you only need a few additional options or drivers, this method can be used to maintain the differences from GENERIC.

Note

When using the include directive, its behavior is similar to C's #include, inserting the entire contents of the target file at the current position. Taking GENERIC as an example, its first line is include "std.amd64", which introduces the standard amd64 base configuration. Using this method, the local configuration file represents the differences from the GENERIC kernel. During upgrades, unless explicitly excluded using nooptions or nodevice, new features added to GENERIC will also be added to the local kernel.

Common Configuration Directives

Directive
Purpose

machine

Specify the target architecture

ident

Specify the kernel name for identification

options

Enable manually configured kernel options

nooptions

Disable unneeded kernel options

device

Compile and include device drivers

nodevice

Remove a specific device driver

makeoptions

Pass build parameters to the Makefile

The above example is from the 16.0-CURRENT GENERIC kernel configuration file. An important difference between -CURRENT and -STABLE/-RELEASE is that -CURRENT introduces extensive debug features. This functionality is introduced by include "std.debug", and some users may notice a significant performance degradation on -CURRENT systems.

Next, remove these debug features to customize a standard configuration kernel. Edit the /usr/src/sys/amd64/conf/MYKERNEL file and add a comment symbol before include "std.debug" to disable these debug features.

Tip

You can also use the following command

to accomplish the above replacement. This is more practical in script files.

Building and Installing

Building the Kernel

After saving the edits to the custom configuration file, you can compile the kernel source code following these steps.

Compiling the kernel must be done in the /usr/src directory, which depends on the SYSDIR environment variable setting (can override /usr/src/sys). Switch to this directory:

For PkgBase systems: You must build the complete user space first. The kernel and user space must be built as a repo package before installation. Use the following command to build the user space:

Compile the new kernel by specifying the name of the custom kernel configuration file:

Option descriptions:

  • -s: Silent mode, only outputs warnings, errors, and compilation progress information.

  • -j: Parallel compilation to reduce build time; the recommended value is the number of CPU cores, and sysctl -n hw.ncpu can automatically detect this value.

The above command outputs as follows:

Note

The first build may take a long time (typically 10 to 60 minutes depending on hardware performance). The build process generates artifacts in the /usr/obj/usr/src/amd64.amd64/sys/MYKERNEL/ directory.

By default, all kernel modules are recompiled when compiling a custom kernel. If you need to speed up kernel updates or only build custom modules, you can edit the /etc/make.conf file before building the kernel.

For example, the following variable specifies the list of modules to build instead of building all modules by default:

Alternatively, this variable lists modules to exclude from the build process:

Installing the New Kernel on Traditionally Installed Systems

Warning

Installing the new kernel will replace the current kernel in /boot/kernel/, and the old kernel is moved to /boot/kernel.old/. If the new kernel is misconfigured and cannot boot, you need to manually specify kernel.old in the boot loader to start. In remote management scenarios, ensure you have out-of-band management or physical access before performing this operation.

Install the new kernel associated with the specified configuration file. The new kernel and kernel modules are installed to the /boot/kernel directory, the old kernel is moved to /boot/kernel.old/kernel, and the old kernel modules remain in the /boot/kernel.old directory:

If the build is successful, the last output of the command should be as follows:

After installation, you need to reboot to load the new kernel:

Installing the New Kernel on PkgBase Systems

Warning

Currently it is not possible to build only the kernel.

Build the PkgBase package for the kernel:

The PkgBase build results will be located under /usr/obj/usr/src/repo/FreeBSD:16:amd64/ (or the corresponding ABI directory), and can be added to a software repository or installed directly.

Verifying the New Kernel

After rebooting, verify that the custom kernel was successfully loaded with the following command:

The uname -a output should display the custom kernel name MYKERNEL.

Verify that CURRENT's debug options are not enabled, such as WITNESS:

There should be no output.

Common Issues During Building

Kernel Boot Failure (Kernel Panic)

If the new kernel cannot boot, you can select to boot the old kernel at the loader prompt. The system keeps the previously installed kernel in /boot/kernel.old/ by default.

Press Esc at the FreeBSD boot menu (loader) to enter the loader prompt (OK prompt), and execute:

You can also directly specify booting the old kernel:

After entering the system, restore the old kernel as the default:

Warning

This operation will replace the current kernel. If kernel.old also has issues, the system will not be able to boot and there will be no other kernel to fall back to. It is recommended to confirm that the kernel.old kernel can boot normally before executing.

config Failure

If config fails, the line number of the error will be displayed. For example, for the following message:

Please ensure the content written on line 17 is correct; you can compare it with the GENERIC or NOTES file.

make Failure

If make fails, it is usually due to an error in the kernel configuration file that is not significant enough to trigger config detection. Please check the configuration file.

Kernel Cannot Boot

If the new kernel cannot boot or cannot recognize devices, you can select the kernel to boot in the FreeBSD boot loader. When the system boot menu appears, select the "Escape to a loader prompt" option. At the prompt, type boot kernel.old, or type the name of any kernel known to boot properly.

After booting with an available kernel, check the configuration file and rebuild. /var/log/messages records kernel information for each successful boot, and dmesg will display the current boot kernel information.

Warning

Replacing the kernel is irreversible. If kernel.old also has issues, the system will not be able to boot and there will be no other kernel to fall back to. kernel.old is the old kernel backup automatically retained by the system when installing a new kernel. It is recommended to confirm that the fallback kernel can boot normally before executing.

Kernel Works, but User Space Commands Are Unavailable

If the kernel version differs from the build version of the system tools, for example, installing a kernel built from -CURRENT source on a -RELEASE system, many system status commands (such as ps and vmstat) will not function properly. To resolve this, you must recompile and install the user space matching the kernel version. Using a kernel with a different version from the rest of the operating system is not recommended.

Build Error: Missing Dependencies

If only the kernel source code is installed without the complete source tree, building kernel modules may fail. You can install the complete source code with the following command:

Resuming After Compilation Interruption

After a build interruption, simply re-execute the same make buildkernel command. The build system will automatically detect completed parts and continue compiling from the interruption point.

If a complete rebuild is needed (for example, after modifying header files), you can first clean the build artifacts:

References

Last updated