How Linux boots, and what to do when it does not
How Linux boots, from firmware through GRUB, the kernel and initramfs to systemd targets. Read boot logs, find slow boots, and recover a server that won't start.
By the end of this lesson you'll be able to
0 of 6 completeThe boot, stage by stage
Between pressing power and seeing a login prompt, five programs take turns. Each one finds and starts the next:
| Stage | Does | Fails like |
|---|---|---|
| Firmware (UEFI, BIOS) | Tests hardware, finds a boot disk | "No bootable device" |
| Boot loader (GRUB) | Loads the kernel and initramfs from /boot | grub rescue> prompt |
| Kernel | Starts drivers, mounts the initramfs | Kernel panic |
| initramfs | Finds the real root filesystem and mounts it | Drops to an (initramfs) shell |
| systemd (PID 1) | Starts services up to the default target | Emergency mode, a failed service |
On a cloud VM you never see the screen, so the first two stages are the provider's job, and almost
every boot problem you'll fix is in the last one: systemd and /etc/fstab.
The boot loader: GRUB
GRUB shows the boot menu (it's hidden on most servers) and starts the kernel with its options. You
never edit its generated file, /boot/grub/grub.cfg. You edit the settings and regenerate it:
# /etc/default/grub (excerpt) GRUB_DEFAULT=0 # which menu entry boots: 0 is the newest kernel GRUB_TIMEOUT=5 # seconds the menu waits GRUB_CMDLINE_LINUX_DEFAULT="quiet" # options passed to the kernel at every boot
$ sudo nano /etc/default/grub $ sudo update-grub # Ubuntu, Debian: rebuild grub.cfg $ sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL family $ cat /proc/cmdline # the options the running kernel booted with $ ls /boot # kernels (vmlinuz-*) and initramfs images (initrd.img-*)
sudo nano /etc/default/grubsudorun the next command as root
cat /proc/cmdlinecatprint a file
ls /bootlslist files
Package upgrades install new kernels and update GRUB for you, keeping the previous kernel. If a new kernel misbehaves, pick the older one from the GRUB menu (hold Shift or press Esc at boot) under Advanced options.
systemd targets
When the kernel and initramfs are done, they start systemd as process 1. systemd boots towards a target: a named group of units that make up a system state. They replaced the old runlevels:
| Target | Old runlevel | State |
|---|---|---|
poweroff.target | 0 | off |
rescue.target | 1 | single user, minimal services, for repairs |
multi-user.target | 3 | a normal server: networking, services, no desktop |
graphical.target | 5 | multi-user plus a desktop |
reboot.target | 6 | reboot |
emergency.target | root shell, almost nothing mounted |
$ systemctl get-default # the target this machine boots into graphical.target $ sudo systemctl set-default multi-user.target # servers don't need a desktop $ sudo systemctl isolate rescue.target # switch now (this stops most services) $ systemctl list-dependencies multi-user.target | head
systemctl get-defaultsystemctlcontrol systemd services
sudo systemctl set-default multi-user.targetsudorun the next command as root
systemctl list-dependencies multi-user.target | head|pipe: send output to the next commandheadshow the first lines
Your services join the boot through targets: the WantedBy=multi-user.target in a unit file (from
the Services with systemd lesson) is what enable links them into.
Reading boot logs
$ journalctl --list-boots # every boot the journal remembers IDX BOOT ID FIRST ENTRY LAST ENTRY -1 7c1d93e0a4b24f6e8d1f2a3b5c6d7e8f Sun 2026-10-11 05:47:21 UTC Mon 2026-10-12 23:58:02 UTC 0 bea482c8f6f84fe4b096e875c660d327 Tue 2026-10-13 00:01:15 UTC Tue 2026-10-13 05:29:13 UTC $ journalctl -b # this boot, from the first kernel line $ journalctl -b -1 -p err # errors from the previous boot $ journalctl -b -1 -n 50 # the last 50 lines before the last reboot $ sudo dmesg -T | grep -iE "error|fail|oom" # kernel messages with readable times $ systemctl --failed # units that failed this boot
journalctl --list-bootsjournalctlread the systemd journal (logs)
journalctl -b-bsince this boot
journalctl -b -1 -p err-pminimum priority, like err
journalctl -b -1 -n 50-nhow many lines
sudo dmesg -T | grep -iE "error|fail|oom"sudorun the next command as root|pipe: send output to the next commandgrepfind lines that match a pattern
systemctl --failedsystemctlcontrol systemd services
journalctl -b -1 is how you answer "why did it reboot at 3 am?". It needs the journal on disk:
check that /var/log/journal/ exists (it does on modern Ubuntu). The end of the previous boot often
shows the cause: an out-of-memory kill, a kernel panic, or a clean reboot someone ran.
Why is the boot slow?
$ systemd-analyze # total boot time Startup finished in 2.1s (kernel) + 8.939s (userspace) = 11.039s graphical.target reached after 8.939s in userspace. $ systemd-analyze blame | head -5 # units by start time $ systemd-analyze critical-chain # the chain the boot actually waited on
systemd-analyze blame | head -5|pipe: send output to the next commandheadshow the first lines
blame can mislead: a slow unit that runs in parallel doesn't delay anything. critical-chain
shows what the boot was really waiting for. A common culprit on servers is
systemd-networkd-wait-online.service waiting for a network interface that never comes up.
When a server will not boot
| You see | Usually | Fix |
|---|---|---|
| "You are in emergency mode" | A bad /etc/fstab line | Log in as root, mount -o remount,rw /, fix /etc/fstab, systemctl daemon-reload, reboot |
(initramfs) prompt | Root filesystem not found or damaged | fsck the root device from that shell, or boot the previous kernel |
| Boot hangs on a "start job" | A service or mount waiting forever | Boot into rescue, find it with journalctl -b -1, fix or disable it |
| Kernel panic after an upgrade | The new kernel or its initramfs | Pick the previous kernel in GRUB, then reinstall or remove the new one |
To reach a root shell when you have a console but no password: at the GRUB menu press e, add
systemd.unit=rescue.target to the end of the line starting with linux, and press Ctrl+X. That
one change boots without most services, so you can repair things.
Write a boot report for a server that rebooted
A server restarted overnight and the team wants to know when, why, and whether everything came back. Collect the boot history, errors from the previous boot, units that failed this boot, and the boot time, into one file.
~/boot-report.txtReveal one safe solution
$ cat > ~/boot-report.sh <<'EOF' #!/bin/bash echo "== boots"; journalctl --list-boots --no-pager | tail -3 echo "== up since: $(uptime -s)" echo "== default target: $(systemctl get-default)" echo "== last lines of the previous boot"; journalctl -b -1 -n 15 --no-pager 2>/dev/null || echo "(no previous boot stored)" echo "== errors in the previous boot"; journalctl -b -1 -p err --no-pager 2>/dev/null | tail -10 echo "== failed units now"; systemctl --failed --no-legend --no-pager echo "== boot time"; systemd-analyze 2>/dev/null | head -1 EOF $ bash ~/boot-report.sh > ~/boot-report.txt $ head -8 ~/boot-report.txt == boots IDX BOOT ID FIRST ENTRY LAST ENTRY 0 bea482c8f6f84fe4b096e875c660d327 Sun 2026-10-11 05:47:21 UTC Tue 2026-10-13 05:29:13 UTC == up since: 2026-10-11 05:47:21 == default target: graphical.target == last lines of the previous boot (no previous boot stored) == errors in the previous boot
cat > ~/boot-report.sh <<'EOF'catprint a file>write output to a file, replacing it
head -8 ~/boot-report.txtheadshow the first lines
Run it with sudo to include the system journal on machines where your user can't read it. The
last lines of the previous boot are the part to read closely: a clean shutdown ends with systemd
stopping services; a crash or power loss just stops.
Try it yourself
0 of 6 steps doneRun each task in your own account or terminal, then tick it off.
Quick check
You edited /etc/default/grub. What makes the change take effect at the next boot on Ubuntu?
Official reading for this lesson
Or get every quiz answer right and it completes itself.