Embedded Linux Boot Time Optimization
A field guide to profiling and cutting boot time — from bootloader hand-off through systemd service ordering to first user-space process.
Why boot time matters
For consumer devices, every second of boot time is user-visible. For industrial equipment, it's availability. For safety-critical systems, it may be a certification requirement. Boot time is a product quality metric, not a development detail.
Phase 1 — Bootloader
Profile: Add timestamps in U-Boot with CONFIG_BOOTSTAGE=y and CONFIG_BOOTSTAGE_REPORT=y.
Quick wins:
- Reduce DDR training time — use stored training results if your SoC supports it (i.MX6/8 DDR calibration, Rockchip ddr_init)
- Cut console baud rate negotiation — fix baud rate in config instead of auto-detecting
- Skip unnecessary peripherals — disable USB, Ethernet PHY init, splash screen if not required for your target
- Enable NAND/eMMC fast boot — configure
CONFIG_FASTBOOTor eMMC HS200/HS400 mode
Target: U-Boot should hand off to the kernel in < 500 ms for most embedded platforms.
Phase 2 — Kernel
Profile: Add initcall_debug to kernel cmdline, capture output, sort by duration.
Or use bootgraph.py on dmesg output:
dmesg | perl scripts/bootgraph.pl > boot.svg
Quick wins:
- Reduce initcall overhead — disable drivers for hardware you don't have (
make menuconfig→ trim to your BOM) - Use
CONFIG_INITRAMFS_SOURCE— embed a minimal initramfs instead of separate rootfs mount - Async driver probing — mark independent drivers with
PROBE_PREFER_ASYNCHRONOUS(kernel 5.x+) - Defer non-critical subsystems — move audio, GPU, USB host init to after user-space is up
Phase 3 — systemd / init
Profile:
systemd-analyze
systemd-analyze blame
systemd-analyze critical-chain
systemd-analyze plot > boot.svg
Quick wins:
- Audit
WantedBy=multi-user.target— every service that boots unnecessarily adds to critical path - Add
Type=notify+ socket activation — services that support it start only when their socket receives a connection - Mask unused units —
systemctl mask bluetooth.serviceif you have no Bluetooth - Set
DefaultTimeoutStartSec— prevent hung services from blocking the boot sequence indefinitely
Phase 4 — Application startup
- Defer non-essential initialization (analytics, telemetry upload, UI polish) until after the device is "operational"
- Use
sd_notify(0, "READY=1")to signal systemd as early as possible, even before full initialization - Pre-compile Python/interpreted startup scripts or replace with compiled binaries
Measurement
Always measure boot time with a hardware timer or GPIO toggle — kernel logs have variable latency. A logic analyzer or oscilloscope on a GPIO that goes high at "first user-space ready" is the ground truth.
Realistic targets
| Platform | Bootloader | Kernel | User-space | Total | |---|---|---|---|---| | Cortex-A53, eMMC | 300 ms | 800 ms | 500 ms | ~1.6 s | | i.MX8, NVMe | 200 ms | 600 ms | 400 ms | ~1.2 s | | Raspberry Pi 4 | 2–3 s | 3–4 s | 2 s | ~7 s (stock) | | RPi 4 optimized | 300 ms | 1 s | 500 ms | ~1.8 s |