The Linux boot process is a carefully orchestrated sequence where the hardware initializes, the kernel loads, and userspace services come online, ultimately presenting you with a login prompt.
Let’s see it in action. Imagine booting a typical x86 system.
First, the BIOS/UEFI (Basic Input/Output System or Unified Extensible Firmware Interface) takes control. It’s firmware embedded on a chip on your motherboard. Its job is to perform a Power-On Self-Test (POST) to check essential hardware, then locate a bootable device. This could be your hard drive, an SSD, a USB drive, or even a network interface. The BIOS/UEFI then loads the bootloader from the Master Boot Record (MBR) or the EFI System Partition (ESP) of that device into memory and executes it.
The bootloader, most commonly GRUB (GRand Unified Bootloader) on Linux systems, is a small program whose sole purpose is to load the Linux kernel. GRUB presents a menu (if configured) allowing you to choose which operating system or kernel version to boot. Once you select an option, GRUB loads the specified kernel image and the initial RAM disk (initrd/initramfs) into memory.
The kernel is the core of the operating system. Upon execution, it first initializes essential hardware components, detects attached devices, and sets up memory management. The initrd/initramfs is a temporary root filesystem loaded into RAM. It contains necessary modules and tools to mount the actual root filesystem. Think of it as a mini-environment that helps the kernel prepare for the main event.
Once the kernel has successfully mounted the real root filesystem (e.g., /dev/sda1), it executes the init process (historically SysVinit, now more commonly systemd). This is the very first userspace process, with process ID (PID) 1. The init process is the ancestor of all other processes. It reads its configuration files (e.g., /etc/inittab for SysVinit or unit files for systemd) to determine which services to start.
Systemd, the modern init system, uses a dependency-based approach. It reads unit files (e.g., .service, .target) which describe services, devices, mount points, and their dependencies. It starts services in parallel based on these dependencies, aiming for a faster boot. For example, it might start the network.target to bring up network interfaces, then local-fs.target to mount local filesystems, and finally multi-user.target which brings the system to a state ready for multiple users, typically presenting a login prompt.
The boot process continues with various system services starting up. These include networking daemons, logging services (like rsyslog or journald), device managers (udev), and graphical display servers (like X.org or Wayland) if a graphical environment is configured. Each service starts based on its defined dependencies and configuration.
Finally, the login manager (e.g., GDM for GNOME, LightDM for many others, or a simple TTY prompt) appears. This is the interface you interact with to authenticate yourself. Once you log in, your user session is initiated, and you gain access to the system’s resources and applications.
The most surprising true thing about the Linux boot process is that the kernel itself doesn’t directly interact with hardware devices in userspace; it relies on kernel modules and drivers, many of which are loaded dynamically from the initrd/initramfs or later from the root filesystem, to abstract hardware complexity.
The next concept you’ll likely encounter is understanding how to customize the boot process, such as configuring GRUB or managing systemd services.