Boot Timing: Measure, Modify, Optimize
Time estimate: ~45 minutes Prerequisites: SSH Login
Learning Objectives
By the end of this tutorial you will be able to:
- Measure boot time per stage using
systemd-analyze - Identify the critical path in the boot sequence
- Make one measurable improvement to boot time
- Interpret
dmesgtimestamps for kernel boot stages
Understanding the Linux Boot Stages
An embedded Linux system boots in five sequential stages, each handing control to the next:
- ROM boot code -- hardwired in silicon, finds the bootloader on storage
- Bootloader (U-Boot / RPi firmware) -- initializes DRAM and clocks, loads the kernel into memory
- Linux kernel -- probes hardware via device tree, loads drivers, mounts the root filesystem
- Init system (systemd) -- starts services in dependency order until the default target is reached
- Application -- your product code begins running
Each stage has its own debugging tools: the bootloader is visible only on the serial console, kernel issues appear in dmesg, and service-level problems show up in journalctl. The command systemd-analyze blame ranks services by startup time, revealing the slowest ones. Only services on the critical chain (the longest sequential dependency path) actually block boot -- disabling services that run in parallel with slower ones has no effect.
For a deeper treatment of boot architectures and optimization strategies, see the Boot Flow and Architectures reference.
1. Baseline Measurement
Connect to your Raspberry Pi via SSH and run:
You should see output similar to:
Startup finished in 2.345s (firmware) + 1.890s (loader) + 4.567s (kernel) + 12.345s (userspace) = 21.147s
graphical.target reached after 12.123s in userspace.
Record each value. These four stages represent:
| Stage | What Happens |
|---|---|
| firmware | GPU/bootloader initialization before the Linux kernel starts |
| loader | Bootloader (U-Boot or RPi firmware) loads the kernel into memory |
| kernel | Kernel decompresses, initializes hardware, mounts root filesystem |
| userspace | systemd starts services until the default target is reached |
Checkpoint
You have a baseline measurement. Write these numbers down — you will compare them later after making changes.
Stuck?
If systemd-analyze returns an error, make sure systemd is the init system. On Raspberry Pi OS this is the default. On minimal images (e.g., Buildroot), systemd may not be present.
2. Service Blame
Find out which services take the longest to start:
This lists all services sorted by startup time (slowest first). Identify the top 5 slowest services and fill in this table:
| Rank | Service | Time | What it Does |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 |
Common slow services on Raspberry Pi and what they do:
| Service | Purpose |
|---|---|
NetworkManager-wait-online.service |
Waits until network is fully connected |
apt-daily.service |
Automatic package update check |
bluetooth.service |
Bluetooth hardware initialization |
avahi-daemon.service |
mDNS/DNS-SD service discovery (Bonjour) |
ModemManager.service |
Cellular modem management |
raspi-config.service |
First-boot configuration checks |
Tip
Not all slow services are unnecessary. A service like NetworkManager-wait-online may be critical if your application needs network access at startup.
3. Critical Chain
The blame command shows individual service times, but services run in parallel. Think of it like a project timeline: if task A (3 s) and task B (5 s) run simultaneously, total time is 5 s, not 8 s. A slow service in blame that runs in parallel with an even slower one is not a bottleneck — it is not on the critical path. The critical chain shows which services are actually blocking boot completion:
Example output:
graphical.target @12.123s
└─multi-user.target @12.120s
└─bluetooth.service @8.456s +3.654s
└─dbus.service @7.234s +0.112s
└─basic.target @7.100s
└─sockets.target @7.098s
└─...
Read this from bottom to top. The critical path is the longest chain of sequential dependencies. A service only appears on the critical path if it is the bottleneck at that point — disabling services not on this path will not improve boot time.
Tip
The @ value shows when a service started (wall-clock time since boot). The + value shows how long it ran. A service with a large + on the critical chain is your best optimization target.
4. Visualize Boot
Generate a boot timeline as an SVG image:
Transfer the file to your PC:
Open boot.svg in a web browser. You will see a horizontal timeline with:
- Red bars — services on the critical path
- Grey bars — services that ran in parallel (not blocking boot)
- Vertical dashed lines — phase boundaries (kernel, early boot, basic.target, etc.)
Annotate the SVG (take a screenshot and add notes) identifying:
- The longest red bar (biggest bottleneck)
- Services that could be disabled
- Services that could be started later (delayed)
Checkpoint
You should have an annotated boot timeline image. This is part of your deliverable.
5. Kernel Boot Stages
The kernel boot process happens before systemd starts. Examine it with:
Look for key timestamps:
[ 0.000000] Booting Linux on physical CPU 0x...
[ 0.000000] Linux version 6.6...
...
[ 1.234567] spi-bcm2835 ...
[ 2.345678] EXT4-fs (mmcblk0p2): mounted filesystem...
[ 3.456789] Run /sbin/init as init process
Identify and record these milestones:
| Event | Timestamp | Description |
|---|---|---|
| Kernel start | [0.000000] |
First kernel log message |
| Driver init complete | [ ? ] |
Last major driver probe message |
| Root filesystem mounted | [ ? ] |
EXT4-fs ... mounted filesystem or similar |
| Init handoff | [ ? ] |
Run /sbin/init — kernel passes control to systemd |
Tip
Use dmesg | grep -i mount to find filesystem mount messages, and dmesg | grep init to find the init handoff point.
6. Optimize: Disable a Service
Pick a non-essential service from your blame list or critical chain. Good candidates on a lab Raspberry Pi:
bluetooth.service— if you are not using Bluetoothavahi-daemon.service— if you do not need network discoveryModemManager.service— if there is no cellular modem
Disable it:
Reboot and re-measure:
Fill in the comparison table:
| Metric | Before | After | Difference |
|---|---|---|---|
| Firmware (s) | |||
| Loader (s) | |||
| Kernel (s) | |||
| Userspace (s) | |||
| Total (s) | |||
| Disabled service | — | — |
Checkpoint
Your userspace time should be measurably lower. If the total did not change, the service you disabled was probably not on the critical path. Try a different one.
Stuck?
If boot time did not change, re-run systemd-analyze critical-chain before and after to confirm whether your target service was on the critical path. Only services on the critical chain affect total boot time.
7. Optional: Kernel Cmdline
You can reduce kernel boot output (and save a small amount of time) by adding the quiet flag:
cmdline.txt contains the kernel command line — a single line of space-separated parameters passed to the Linux kernel at boot. These parameters control kernel behavior: root filesystem location, console output, debugging options, and more. The entire content must be on one line (no line breaks).
Add quiet to the end of the existing line (do not create a new line):
Reboot and measure again:
The quiet flag suppresses most kernel messages during boot. This saves time spent on console output, especially on serial consoles. The effect is typically small (0.1--0.5 s) but measurable.
Tip
The quiet flag does not delete log messages. They are still available via dmesg after boot. It only suppresses printing them to the console during startup.
What Just Happened?
You measured and optimized the boot process on a real embedded Linux system:
systemd-analyze timegave you the high-level breakdown: firmware, loader, kernel, userspacesystemd-analyze blameidentified which individual services consume the most timesystemd-analyze critical-chainrevealed which services actually block boot (the critical path)systemd-analyze plotproduced a visual timeline for analysisdmesgshowed what the kernel does before systemd even starts- Disabling a service demonstrated that only critical-path changes affect total boot time
This is the same workflow used in automotive, industrial, and consumer electronics to meet boot time requirements. Production systems often target sub-2-second boot using techniques like kernel trimming, initramfs optimization, and custom init systems.
Challenges
Challenge 1: Boot Timestamp Script
Write a shell script that runs at boot (via a systemd service) and logs the current timestamp to /tmp/boot_ready.log. After boot, compare this timestamp to uptime to measure the time from power-on to your application being ready. Hint: create a simple .service file with Type=oneshot and ExecStart=/bin/bash -c 'date +%s > /tmp/boot_ready.log'.
Challenge 2: Parallel Service Analysis
Use systemd-analyze plot to identify services that currently start sequentially but have no dependency on each other. Could they run in parallel? Document which services you would modify and what the expected time saving would be.
Challenge 3: Compare to Buildroot
If you have a Buildroot image from a previous tutorial, measure its boot time using manual methods (since it may not have systemd). Use dmesg timestamps and a stopwatch. How does it compare to the full Raspberry Pi OS?
Deliverable
Submit the following:
| Item | Description |
|---|---|
| Annotated boot timeline | Screenshot of boot.svg with annotations showing bottlenecks |
| Before/after table | Completed comparison table from Step 6 |
dmesg milestone table |
Completed kernel boot stages table from Step 5 |
| Service blame table | Top 5 slowest services with descriptions from Step 2 |