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

Inside the FreeBSD Boot Process: BIOS/UEFI, the Loader, and init

A stage-by-stage walkthrough of how a FreeBSD machine goes from power-on to a login prompt, and where to intervene at each step.

FreeBSD’s boot sequence is unusually transparent compared to most modern operating systems — each stage is a distinct, inspectable program, and the loader in particular gives you a real interactive prompt with its own scripting language before the kernel ever runs. Understanding the stages makes the difference between a five-minute fix and a full reinstall when something goes wrong.

Stage 1 and 2: the boot blocks

On BIOS systems, the first sector of the boot device holds boot0, a minimal 512-byte boot manager whose only real job is to load boot1, which in turn understands enough of the filesystem to find and load the third stage loader. On UEFI systems, this is replaced by loader.efi running directly as an EFI application, but the role is the same: locate and hand off to the loader as quickly as possible.

# Inspect the boot manager installed on a disk (BIOS/MBR systems)
gpart show da0

Stage 3: /boot/loader

This is where FreeBSD diverges most from Linux’s GRUB. /boot/loader is a full environment with its own Forth-derived scripting language, capable of reading UFS and ZFS directly, loading kernel modules, and presenting an interactive menu:

\   FreeBSD/x86 boot
\   Default: boot
\   Autoboot in 3 seconds

Interrupting autoboot drops you into loader’s prompt, where you can inspect and change what’s about to happen before a single line of kernel code executes:

OK boot -s          # boot to single-user mode
OK boot kernel.old   # boot the previous kernel
OK set hint.hpt27xx.0.disabled=1
OK show              # dump all currently set loader variables

/boot/loader.conf is the persistent counterpart to those interactive commands — kernel modules to preload, tunables to set before the kernel even starts, since some sysctls (particularly hardware and memory-related ones) can only be configured this early:

# /boot/loader.conf
zfs_load="YES"
vfs.zfs.arc_max="2G"

Stage 4: the kernel and devd

Once the loader hands off, the kernel initializes hardware, mounts the root filesystem specified by vfs.root.mountfrom (or auto-detected), and starts PID 1: /sbin/init. Before that, though, the kernel’s device enumeration triggers devd, the device state change daemon, which reacts to hotplug events (a USB drive inserted, a network link going up) according to rules in /etc/devd.conf.

Stage 5: init and rc

init is deliberately minimal — its job is to read /etc/ttys and start /etc/rc, FreeBSD’s system startup script. Unlike Linux’s systemd, FreeBSD’s rc system is a collection of interdependent shell scripts under /etc/rc.d and /usr/local/etc/rc.d, each declaring its dependencies via REQUIRE/BEFORE comments that rcorder topologically sorts before execution:

#!/bin/sh
# PROVIDE: nginx
# REQUIRE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr
name="nginx"
rcvar="nginx_enable"
load_rc_config $name
run_rc_command "$1"

Whether a given service actually starts is controlled entirely by /etc/rc.confrc.d scripts are present regardless, but each checks its own _enable variable before doing anything:

# /etc/rc.conf
nginx_enable="YES"
sshd_enable="YES"
service nginx start
service nginx status
rcorder /etc/rc.d/* 2>/dev/null   # inspect the computed start order

Single-user mode: the escape hatch

Booting with -s (either from the loader prompt or by setting it as a boot option) drops straight to a root shell after the kernel loads, skipping rc entirely — the standard recovery path for a broken fstab, a forgotten root password, or a service that hangs the boot sequence. From there, mount -u / remounts root read-write, and a normal multi-user boot can be resumed with exit.

Why the separation matters in practice

Because each stage is independently swappable and inspectable, diagnosing a boot failure is a process of elimination rather than guesswork: does it get to the loader prompt at all (stage 3 is fine, check the disk/BIOS), does the kernel load but panic (a driver or hardware issue), or does it reach multi-user but a specific service fails (an rc.d problem, isolated with verbose_loading="YES" in rc.conf)? That clean separation of concerns — boot manager, loader, kernel, init, rc — is arguably FreeBSD’s most underappreciated design decision.