Synopsis #
The GENERIC kernel is the maintainable default for most FreeBSD systems. A runtime setting, loadable module, or documented loader tunable usually solves a specific requirement without creating a separate kernel build and upgrade path.
A custom kernel is justified when a required facility cannot be supplied as a module, a controlled security design forbids runtime module loading, kernel development requires different build options, or a measured resource constraint cannot be addressed more narrowly. Smaller is not automatically safer or faster. Removing an unfamiliar driver or subsystem can also remove a boot, storage, console, or recovery dependency.
Choose the narrowest mechanism #
Classify the requirement before changing the boot path:
| Requirement | First choice | Persistence boundary |
|---|---|---|
| Change writable runtime kernel state | A documented sysctl |
/etc/sysctl.conf after a measured runtime test |
| Supply a driver or facility dynamically | A documented kernel module | Load at boot only after a runtime qualification |
| Set a value required before the kernel completes startup | A documented loader tunable | /boot/loader.conf, with console recovery available |
| Enable a facility that has no loadable implementation | A custom kernel option or device | Local KERNCONF maintained with matching source |
| Prevent runtime module loading as part of a defined security model | A reviewed custom kernel and module policy | Kernel build, boot, and recovery procedure |
| Develop or debug kernel code | A purpose-built development kernel | Separate from the production rollback kernel |
The system-setting guide
distinguishes sysctl.conf, loader.conf, and rc.conf. A tunable should not be moved into a custom kernel merely to make it harder to change, and an ordinary service setting does not belong in the kernel at all.
Keep GENERIC when it satisfies the requirement #
GENERIC receives the broadest release testing and carries drivers and facilities needed by varied hardware and recovery environments. Retain it when the proposed custom build would only:
- remove unused drivers without a measured memory constraint;
- reduce boot messages or cosmetic probe activity;
- replace a module that FreeBSD already loads on demand;
- copy an old kernel configuration forward unchanged;
- apply speculative performance options or undocumented tuning advice.
Record the running base and kernel versions before investigating a difference:
$ freebsd-version -kru
$ uname -m
$ kldstat
freebsd-version -kru helps expose a kernel and userland that are not at the same patch level. A third-party kernel module must also match the relevant kernel interfaces. The package branch guide
covers the additional alignment required by packaged modules such as graphics drivers.
Prefer a loadable module for an isolated facility #
The manual page for a driver or subsystem normally states whether a module exists, how it is named, and whether it may be loaded through loader.conf(5). Confirm that documentation and inspect the current state before changing it:
$ kldstat
$ ls /boot/kernel
Loading a module into a running system is not automatically harmless. A network, storage, security, or filesystem module can attach devices or begin enforcing policy immediately. Qualify its effect on a non-production system or in a maintenance window, and capture the pre-change state. Do not assume that every module can be unloaded safely after it has attached resources.
If the module must be present before devices attach or services start, use the exact boot setting documented by its manual page. A persistent module load changes the boot path, so retain access to the loader menu or system console and know which line must be bypassed.
Use a loader tunable only when the interface requires it #
Loader tunables are evaluated before or while the kernel initializes. They are appropriate for settings whose manual page explicitly identifies them as tunables or whose effect is needed before the corresponding subsystem starts.
Many loader values cannot be changed meaningfully after boot. Conversely, many writable runtime sysctls do not need an early boot assignment. Confirm the distinction in the owning manual page rather than inferring it from the assignment syntax.
Before adding a loader value:
- State the observed failure or requirement.
- Record the current value and the source that documents the proposed value.
- Change one boundary at a time.
- Arrange a maintenance window and console recovery.
- Verify the value and the original symptom after reboot.
Do not add a generic tuning file containing copied values. Settings for mbuf capacity, queues, timers, and memory allocation require workload evidence. For example, the mbuf troubleshooting guide
requires allocator evidence before considering kern.ipc.nmbclusters.
Justify a custom kernel explicitly #
A custom kernel becomes an operational product that must be built, identified, installed, recovered, and updated. A sound justification names both the required change and why a module or documented runtime mechanism cannot supply it.
Reasonable cases include:
- a required kernel option or device has no loadable equivalent;
- an embedded or unusually constrained system has a measured resource limit;
- a controlled appliance design permits only a reviewed static kernel and has a matching recovery process;
- kernel debugging or development requires instrumentation or build options not present in GENERIC.
A custom kernel should not be presented as a universal hardening step. Omitting code does not compensate for unsupported software, excess network exposure, weak privilege boundaries, or untested recovery. Establish the security baseline first.
Treat GENERIC and KERNCONF as separate concepts #
GENERIC is the architecture’s standard kernel configuration. KERNCONF is the build selection that names the configuration to build. Omitting KERNCONF causes the standard GENERIC configuration to be built by the documented source workflow.
Do not edit the source tree’s GENERIC file. For a small additive difference, keep a local configuration that includes GENERIC and declares only the delta:
include GENERIC
ident LOCAL
options REQUIRED_OPTION
This structure inherits additions made to GENERIC during a source update. A copied and heavily reduced configuration instead owns every omission and must be compared with the new GENERIC and NOTES files at each upgrade.
The exact configuration must remain outside an expendable /usr/src checkout or in protected configuration management, with a link into the architecture’s conf directory if required by the build. Record the source revision, architecture, KERNCONF name, build configuration, installed kernel identity, and any externally built modules.
Maintain source, kernel, modules, and userland together #
Build only from source that corresponds to the intended FreeBSD branch and update method. A kernel from a different source generation than its userland can make utilities unreliable, while external modules built against different kernel interfaces may fail to load or operate correctly.
freebsd-update automatically updates only GENERIC. A custom kernel must be rebuilt and installed separately when its kernel source changes; rebuilding after other updates also keeps the reported patch level aligned with the updated system. Before a release upgrade through freebsd-update, preserve the custom kernel and install a matching, unmodified GENERIC kernel as /boot/GENERIC as required by the official upgrade procedure. Do not begin the release transition until that fallback has booted successfully.
For every base-system update or release upgrade:
- Update the source tree to the matching branch and revision.
- Compare the local delta with the release’s GENERIC and
NOTESfiles. - Revalidate the original reason for each local option or omission.
- Build the kernel and its required modules through the current official procedure.
- Preserve a separately named known-good kernel before installing another candidate.
- Rebuild or reinstall external kernel modules against the new kernel as their owning procedure requires.
- Verify boot, storage, console, networking, and the workload that justified the customization.
The official kernel configuration chapter owns the build and installation commands. Keeping those mechanics there avoids copying a release-sensitive procedure into a decision guide.
Preserve an independent rollback path #
installkernel normally moves the previous installed kernel to /boot/kernel.old, but that location is not a durable history. A later installation overwrites it. Preserve a separately named known-good kernel and its compatible modules before installing a new candidate.
Confirm before the change that the loader can select that kernel and that the console remains reachable. A remote-only host should not receive a new kernel, storage driver, or early loader setting without an out-of-band recovery path.
If the candidate does not boot, use the loader to start the known-good kernel. If it boots but userland tools or external modules fail, first verify the source, kernel, module, and userland versions rather than adding compatibility workarounds.