> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/ask/flat/chapter-40-freebsd-kernel-architecture/di-40.6-jie-gou-jian-ding-zhi-nei-he.md).

# 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:

```sh
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:

```ini
u3g_load="YES"
```

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.

```sh
$ fetch https://download.freebsd.org/snapshots/amd64/16.0-CURRENT/src.txz
# tar xvf src.txz -C /
# rm src.txz	# Remove the source archive that is no longer needed
```

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:

```sh
# git clone -b main https://git.FreeBSD.org/src.git /usr/src           # -CURRENT
# git clone -b stable/15 https://git.FreeBSD.org/src.git /usr/src      # 15-STABLE
# git clone -b releng/15.0 https://git.FreeBSD.org/src.git /usr/src    # 15.0-RELEASE
```

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:

```sh
# ls -a /usr/src/sys
.		conf		isa		netlink		rpc
..		contrib		kern		netpfil		security
Makefile	crypto		kgssapi		netsmb		sys
README.md	ddb		libkern		nfs		teken
amd64		dev		modules		nfsclient	tests
arm		dts		net		nfsserver	tools
arm64		fs		net80211	nlm		ufs
bsm		gdb		netgraph	ofed		vm
cam		geom		netinet		opencrypto	x86
cddl		gnu		netinet6	powerpc		xdr
compat		i386		netipsec	riscv		xen
```

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

```sh
# ls /usr/src/sys/amd64
Makefile	conf		linux		pt
acpica		ia32		linux32		sgx
amd64		include		pci		vmm
```

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

```sh
# ls /usr/src/sys/amd64/conf/
DEFAULTS	GENERIC-KCSAN	GENERIC.hints	LINT-NOIP	NOTES
FIRECRACKER	GENERIC-KMSAN	LINT		LINT-NOVIMAGE
GENERIC		GENERIC-MMCCAM	LINT-NOINET	MINIMAL
GENERIC-KASAN	GENERIC-NODEBUG	LINT-NOINET6	MINIMAL-NODEBUG
```

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:

```sh
# cp /usr/src/sys/amd64/conf/GENERIC /usr/src/sys/amd64/conf/MYKERNEL
```

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.

```ini
cpu		HAMMER
ident		GENERIC

makeoptions	DEBUG=-g		# Build kernel with gdb(1) debug symbols
makeoptions	WITH_CTF=1		# Run ctfconvert(1) for DTrace support

options 	SCHED_ULE		# ULE scheduler
options 	SCHED_4BSD		# Original 4.xBSD scheduler
options 	NUMA			# Non-Uniform Memory Architecture support
options 	PREEMPTION		# Enable kernel thread preemption
options 	EXTERR_STRINGS
options 	VIMAGE			# Subsystem virtualization, e.g. VNET

# For full debugger support use (turn off in stable branch):
include "std.debug"

...other options omitted...
```

> **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.

```sh
#include "std.debug"
```

> **Tip**
>
> You can also use the following command
>
> ```sh
> # sed -i '' 's/^include "std.debug"/#include "std.debug"/' /usr/src/sys/amd64/conf/MYKERNEL
> ```
>
> 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:

```sh
# cd /usr/src
```

**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:

```sh
# make -s -j$(sysctl -n hw.ncpu) buildworld KERNCONF=MYKERNEL
```

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

```sh
# make -s -j$(sysctl -n hw.ncpu) buildkernel KERNCONF=MYKERNEL
```

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:

```sh
make[1]: /usr/src/Makefile.inc1:371: SYSTEM_COMPILER: libclang will be built for bootstrapping a cross-compiler.
make[1]: /usr/src/Makefile.inc1:376: SYSTEM_LINKER: libclang will be built for bootstrapping a cross-linker.

--------------------------------------------------------------
>>> Kernel build for MYKERNEL started on Sat May  2 12:36:44 CST 2026
--------------------------------------------------------------
===> MYKERNEL

--------------------------------------------------------------
>>> stage 1: configuring the kernel
--------------------------------------------------------------
Kernel build directory is /usr/obj/usr/src/amd64.amd64/sys/MYKERNEL
Don't forget to do ``make cleandepend && make depend''
        0.13 real         0.06 user         0.07 sys

--------------------------------------------------------------
>>> stage 2.3: build tools
--------------------------------------------------------------
        0.16 real         0.06 user         0.11 sys

--------------------------------------------------------------
>>> stage 3.1: building everything
--------------------------------------------------------------
linking kernel.full
ctfmerge -L VERSION -g -o kernel.full ...
   text	   data	    bss	    dec	    hex	filename
21068157	1732469	8752832	31553458	1e177b2	kernel.full
      986.52 real      7898.76 user      6298.50 sys
--------------------------------------------------------------
>>> Kernel build for MYKERNEL completed on Sat May  2 12:53:11 CST 2026
--------------------------------------------------------------
>>> Kernel(s)  MYKERNEL built in 987 seconds, ncpu: 16, make -j16
--------------------------------------------------------------
```

> **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:

```ini
MODULES_OVERRIDE = linux acpi
```

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

```ini
WITHOUT_MODULES = linux acpi sound
```

### 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:

```sh
# make -s installkernel KERNCONF=MYKERNEL
```

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

```sh
...other output omitted...

--------------------------------------------------------------
>>> Installing kernel MYKERNEL completed on Sat May  2 13:21:43 CST 2026
--------------------------------------------------------------
>>> Install kernel(s) MYKERNEL completed in 9 seconds, ncpu: 16
--------------------------------------------------------------
```

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

```sh
# reboot
```

### 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:

```sh
# make -s -j$(sysctl -n hw.ncpu) packages KERNCONF=MYKERNEL DISTDIR=/usr/obj/dist
```

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:

```sh
$ uname -a
FreeBSD ykla 16.0-CURRENT FreeBSD 16.0-CURRENT #0: Sat May  2 12:39:42 CST 2026     ykla@ykla:/usr/obj/usr/src/amd64.amd64/sys/MYKERNEL amd64
```

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

```sh
$ sysctl kern.bootfile
kern.bootfile: /boot/kernel/kernel
```

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

```sh
$ dmesg | grep -i 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:

```sh
OK unload
OK load /boot/kernel.old/kernel
OK boot
```

You can also directly specify booting the old kernel:

```sh
OK boot /boot/kernel.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.

```sh
# mv /boot/kernel /boot/kernel.bad
# mv /boot/kernel.old /boot/kernel
```

### config Failure

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

```sh
config: line 17: syntax error
```

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.

```sh
# mv /boot/kernel /boot/kernel.bad
# mv /boot/kernel.old /boot/kernel
```

### 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:

```sh
# git clone --depth=1 https://git.FreeBSD.org/src.git /usr/src
```

### 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:

```sh
# make cleandir
# make buildkernel KERNCONF=MYKERNEL
```

## References

* FreeBSD Project. build(7)\[EB/OL]. \[2026-04-30]. <https://man.freebsd.org/cgi/man.cgi?query=build&sektion=7>. FreeBSD source code build system manual page.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.bsdcn.org/ask/flat/chapter-40-freebsd-kernel-architecture/di-40.6-jie-gou-jian-ding-zhi-nei-he.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
