← All chapters
Chapter 5· 16 min read · illustrated

The Boot Process

From a cold power button to a login prompt — the whole handoff chain

When you press the power button, the machine does not magically "have" an operating system running. At that instant there is no kernel in memory, no scheduler, no notion of a file — just a CPU that has been given power and a small amount of code baked into a chip on the motherboard. The boot process is the carefully staged relay race that takes the computer from that bare state all the way to a login prompt, one component waking up and handing control to the next.

Each stage exists to load and start the next, slightly more capable, stage: firmware finds and runs a bootloader, the bootloader finds and runs the kernel, the kernel starts the first process, and that process brings up everything else. It is a chain of handoffs, and knowing the order matters — because when a machine "won’t boot", the fix depends entirely on which link in the chain failed.

This chapter follows that chain end to end. We will keep a developer’s eye on it throughout, because the boot sequence is exactly what a virtual machine reproduces, what a container deliberately skips, and what you are debugging whenever a cloud instance comes up unreachable or a service refuses to start.

01

From power button to login: the big picture

Before we open up any single stage, hold the whole chain in your head at once. Power reaches the CPU, which begins executing firmware burned into a motherboard chip. The firmware tests the hardware, finds a boot device, and loads a small program called the bootloader. The bootloader loads the operating-system kernel into memory and jumps into it. The kernel initializes itself, then starts exactly one user program — the first process, PID 1 — which brings up every service until the machine is usable and shows you a login.

The single unifying theme is the handoff. No stage does everything; each one is just capable enough to locate and start the next, then step aside. Firmware knows almost nothing about your operating system — only how to run a bootloader. The bootloader knows almost nothing about your services — only how to start a kernel. This narrowing of responsibility is what makes the whole thing swappable: you can change your kernel without touching firmware, and change your init system without touching the kernel.

Firmware
Code on a motherboard chip (BIOS or UEFI) that runs first and finds something to boot.
Bootloader
A small program (typically GRUB) whose only job is to load the kernel into memory and start it.
Kernel
The operating-system core; once running it controls all hardware and starts the first process.
PID 1
The first user-space process (init / systemd) that the kernel starts; ancestor of every other process.

Why this order matters: When a box "won’t boot", the first diagnostic question is always: which handoff failed — firmware, bootloader, kernel, or init?

Tap to enlarge
02

Firmware: BIOS and UEFI

The very first code the CPU runs lives in non-volatile flash on the motherboard: the firmware. Its first act is the POST — the Power-On Self Test — a quick check that the CPU, RAM, and essential devices are present and responding. If POST fails badly you get beeps or diagnostic LEDs and no boot at all. Once POST passes, the firmware’s real job begins: consult a configured boot order, walk the candidate devices (internal disk, USB, network), and find something bootable.

How it finds "something bootable" is exactly where the two firmware worlds differ. Legacy BIOS reads the very first 512-byte sector of a disk — the Master Boot Record (MBR) — and blindly executes the tiny bit of boot code stored there. That is all it understands: raw sectors and a fixed-size boot stub. It is simple, decades old, and cramped — the MBR partition scheme also caps disks at 2 TB and four primary partitions.

Modern UEFI is far richer. Instead of a magic sector, it understands a real filesystem: it reads a small FAT-formatted partition called the EFI System Partition (ESP) and runs bootloader programs stored there as ordinary files (for example, /EFI/BOOT/BOOTX64.EFI). Disks use the GPT partition scheme, which lifts the size and partition-count limits. UEFI can also enforce Secure Boot, checking each bootloader’s cryptographic signature against trusted keys before running it — a defense against boot-time malware.

POST
Power-On Self Test — firmware’s initial check that core hardware is present and healthy.
BIOS + MBR
Legacy path: firmware runs boot code from the disk’s first 512-byte sector.
UEFI + GPT + ESP
Modern path: firmware runs a .efi bootloader file from a FAT EFI System Partition on a GPT-partitioned disk.
Secure Boot
A UEFI feature that verifies the bootloader’s signature against trusted keys before executing it.

Engineer’s note: A cloud image built for BIOS won’t boot on a UEFI-only instance and vice versa — the firmware type is part of the image, not an afterthought.

Tap to enlarge
03

The bootloader (GRUB)

The bootloader is the bridge between firmware and the operating system. Firmware can start a program but has no idea what Linux is; the kernel needs to be pulled off disk and placed in memory before it can run. GRUB (the GRand Unified Bootloader) fills that gap. Its job is narrow and specific: locate the kernel image and its initramfs on disk, load both into RAM, pass the kernel its command-line parameters, and jump into the kernel’s entry point.

GRUB loads in stages because the code firmware first runs is tiny. On BIOS systems, a minuscule stage 1 in the MBR knows just enough to load a slightly larger stage 1.5, which understands filesystems well enough to read the full stage 2 and GRUB’s configuration from /boot. Stage 2 is the capable part: it reads its config, can browse files, and drives the menu. On UEFI the .efi image plays the role of the early stages, but the idea is the same — bootstrap from something tiny up to something that can read your /boot directory.

That configuration is what produces the boot menu you sometimes see: a list of kernels you can boot, usually the current one plus older versions to fall back on, and often a "recovery mode" entry. Being able to pick an older kernel is a real safety net — if a kernel upgrade breaks your hardware, you reboot and select the previous one.

A GRUB menu entry (from /boot/grub/grub.cfg) — note the kernel and initrd linesbash
menuentry 'Ubuntu, with Linux 6.8.0-45-generic' {
    linux   /boot/vmlinuz-6.8.0-45-generic root=UUID=1b3a... ro quiet splash
    initrd  /boot/initrd.img-6.8.0-45-generic
}

Key idea: GRUB does not run your OS — it loads the kernel + initramfs and hands over control. After the jump, GRUB is gone from the picture.

Tap to enlarge
04

Loading the kernel & initramfs

The kernel image GRUB loads (vmlinuz) is compressed, so its first act is to decompress itself in memory. Once expanded, it initializes the core of the operating system: it takes full control of the CPU, sets up memory management and the page tables, starts the scheduler, and begins detecting and initializing the hardware it has built-in drivers for. From this moment on, the kernel — not firmware, not GRUB — owns the machine.

But there is a chicken-and-egg problem. To mount the real root filesystem (where all your files and drivers live), the kernel may need drivers that themselves live on that filesystem — a driver for your specific NVMe or RAID controller, or the code to unlock a LUKS-encrypted disk. The kernel can’t read the disk until it has the driver, and it can’t load the driver until it can read the disk.

The initramfs (initial RAM filesystem, historically initrd) breaks this cycle. GRUB loaded it into memory alongside the kernel, and the kernel mounts it as a small, temporary root filesystem entirely in RAM. This mini root contains just enough tools and driver modules to do one job: probe the hardware, load the right storage/encryption drivers, find and mount the actual root filesystem, then pivot onto it and hand off to the first real process. After the pivot, the initramfs has served its purpose and is discarded.

vmlinuz
The compressed, bootable kernel image; it decompresses itself into memory as the first step.
initramfs / initrd
A tiny root filesystem loaded into RAM that carries the drivers needed to reach the real root.
Root filesystem
The real on-disk filesystem mounted at / that holds the full OS and your files.
pivot_root
The switch from the temporary in-RAM root to the real root filesystem once it is mounted.

Why initramfs exists: It solves a bootstrap paradox: the drivers needed to mount the root disk can’t be read from the root disk, so they ride in memory instead.

Tap to enlarge
05

The first process: init / systemd (PID 1)

Once the real root filesystem is mounted, the kernel does one last thing before stepping back into a purely supervisory role: it starts a single user-space program as PID 1. This is the first process, and everything else on the system will be a descendant of it. If PID 1 ever exits, the kernel panics — that is how central it is.

Historically PID 1 was SysV init, which ran shell scripts one after another in a fixed order according to a "runlevel". It worked but was slow and strictly sequential. Today most Linux distributions use systemd, which models the system as a graph of units (services, mount points, sockets, devices) with dependencies between them. Instead of a rigid script order, systemd starts everything it can in parallel, respecting dependencies, until it reaches a named target — the modern replacement for a runlevel (for example, multi-user.target for a normal server, graphical.target for a desktop).

In this stage the system truly comes alive: filesystems from /etc/fstab are mounted, the network is configured, logging starts, and background services (sshd, cron, your database, your web server) are launched. systemd also supervises them — if a service crashes, it can restart it, and it records why it failed.

Inspecting PID 1 and how long boot tookbash
$ ps -p 1 -o pid,comm
  PID COMMAND
    1 systemd

$ systemd-analyze
Startup finished in 3.412s (kernel) + 6.980s (userspace) = 10.393s
graphical.target reached after 6.900s in userspace.

Mental model: PID 1 is the root of the process tree. Every server, shell, and program you ever launch is one of its descendants.

Tap to enlarge
06

Reaching user space & login

With services up and the target reached, the system enters full user space and offers a way to log in. On a text console, systemd starts a small program called getty on each terminal; getty prints the login prompt, takes your username, and hands off to login to check your password. On a desktop, a display manager (like GDM) draws the graphical login screen instead. Either way, the machine is now doing what it exists to do: waiting for a human or a client to use it.

When your credentials check out, the system starts a login shell (such as bash or zsh) or a desktop session under your user account. That shell becomes your entry point into the machine, and — crucially — the parent of every command you run from it. Type ./myserver and it launches as a child of your shell, which traces all the way back up to PID 1.

This is the payoff of the whole chain, and it connects directly to everything else in this course. The program you write and run does not appear from nowhere: it is a process, created under the process tree that the boot sequence built, scheduled by the kernel that GRUB loaded, on hardware the firmware powered up. "Running a program" is really "asking PID 1’s descendant tree to grow one more branch".

getty
The per-terminal program that shows the text login prompt and hands off to login.
Display manager
The graphical equivalent of getty — draws the desktop login screen (e.g. GDM, SDDM).
Login shell
The interactive shell (bash, zsh) started after login; parent of the commands you run.

Tie-back: Every process you start is a descendant of PID 1 — the boot process literally built the tree your program will hang from.

Tap to enlarge
07

Why engineers should care

Knowing the boot chain turns "it won’t come up" from a mystery into a checklist. A kernel panic means the kernel itself failed early — often a bad initramfs, a missing storage driver, or a wrong root= parameter, so it never reached PID 1. A boot that hangs after login-ish messages is usually a service stuck or failing under systemd; systemctl --failed and journalctl -b tell you which unit and why. Locating the failure on the timeline — firmware, bootloader, kernel, or init — tells you which tool to reach for.

This knowledge reshapes how you think about containers versus virtual machines, a distinction that trips up many engineers. A virtual machine boots exactly like a physical one: virtual firmware runs, a bootloader loads a full guest kernel, and that kernel starts its own systemd — a complete boot chain, which is why VMs take tens of seconds to start. A container has no firmware, no bootloader, and no kernel of its own. It shares the host’s already-running kernel and simply starts your application as PID 1 inside an isolated view of the system. That is why containers start in milliseconds: they skip the entire boot process and jump straight to the last stage.

  • A container image is a root filesystem plus a start command — there is nothing to "boot", so its PID 1 is your process (or a tiny init like tini).
  • Because your app is PID 1 in a container, it must handle signals like SIGTERM itself, or graceful shutdown silently breaks.
  • A VM image, by contrast, contains a full bootable OS — firmware type, bootloader, kernel, and init all matter when it fails to start.
  • Cloud instances boot from prebuilt images (AMIs and the like); a machine that comes up unreachable is almost always a bootloader, kernel, or first-boot service problem, debuggable via the serial/system console.

Looking ahead: The boot process ends the moment your program can run. Next we go one level deeper into how that program actually asks the kernel for anything — the system call, the doorway between user space and the kernel.

Tap to enlarge