Skip to content

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 dmesg timestamps for kernel boot stages
Understanding the Linux Boot Stages

An embedded Linux system boots in five sequential stages, each handing control to the next:

  1. ROM boot code -- hardwired in silicon, finds the bootloader on storage
  2. Bootloader (U-Boot / RPi firmware) -- initializes DRAM and clocks, loads the kernel into memory
  3. Linux kernel -- probes hardware via device tree, loads drivers, mounts the root filesystem
  4. Init system (systemd) -- starts services in dependency order until the default target is reached
  5. 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:

systemd-analyze time

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:

systemd-analyze blame

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:

systemd-analyze critical-chain

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:

systemd-analyze plot > boot.svg

Transfer the file to your PC:

# Run this on your PC, not on the Pi
scp linux@192.168.x.y:~/boot.svg .

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:

  1. The longest red bar (biggest bottleneck)
  2. Services that could be disabled
  3. 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:

dmesg | head -40

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 Bluetooth
  • avahi-daemon.service — if you do not need network discovery
  • ModemManager.service — if there is no cellular modem

Disable it:

sudo systemctl disable bluetooth.service
sudo systemctl stop bluetooth.service

Reboot and re-measure:

sudo reboot
# After reconnecting via SSH:
systemd-analyze time

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).

sudo vim /boot/firmware/cmdline.txt

Add quiet to the end of the existing line (do not create a new line):

... rootwait quiet

Reboot and measure again:

sudo reboot
# After reconnecting:
systemd-analyze time
dmesg | tail -5

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:

  1. systemd-analyze time gave you the high-level breakdown: firmware, loader, kernel, userspace
  2. systemd-analyze blame identified which individual services consume the most time
  3. systemd-analyze critical-chain revealed which services actually block boot (the critical path)
  4. systemd-analyze plot produced a visual timeline for analysis
  5. dmesg showed what the kernel does before systemd even starts
  6. 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

Course Overview | Next: Buildroot SDL2 Image →