This closing lecture in our free linux kernel development course init module pulls together everything from BusyBox init, System V init, and systemd’s watchdog, resource control, and logging features into one decision: which init system actually belongs on your board? If you’ve been following this free embedded systems course series, you already know how each one works individually — now we compare them side by side.
What You Will Learn
Three Options, Three Philosophies
By now in this series you’ve built and configured all three approaches. Each one exists because it optimizes for a different constraint, not because one is objectively “better”:
BusyBox init
A tiny, single-binary init built for static, low-complexity systems that boot a fixed set of daemons and stay that way. Minimal storage footprint, minimal moving parts, minimal boot time.
System V init
The historical default, still around mostly because it’s already there and Yocto projects that opt out of systemd fall back to it. It has few concrete advantages over BusyBox init on a modern embedded board.
systemd
Built for systems with real dependency graphs between services, plus extras like the watchdog handling from the previous lecture, resource control via slices, device management through udev, and structured logging via journald.
Decision Table By Constraint
| Your Constraint | Recommended Init | Reasoning |
|---|---|---|
| Fixed set of daemons, boots once and stays static | BusyBox init | No dependency graph needed; simplicity wins |
| Storage under ~10 MiB budget for init + support daemons | BusyBox init | systemd’s minimal footprint alone can exceed the budget |
| Complex startup/runtime dependencies between services | systemd | Native dependency ordering avoids fragile shell-script sequencing |
| Need per-service or hardware watchdog handling | systemd | Built-in, well-tested watchdog subsystem (see previous lecture) |
| Using Yocto and specifically avoiding systemd | System V init | The standard fallback in that ecosystem |
| Fastest possible boot, above all else | BusyBox init with minimal scripts | Nothing beats a lean, purpose-built init for raw boot speed |
| Using Buildroot as the build system | BusyBox init | Buildroot’s default toolchain leans toward BusyBox init as the natural fit |
Boot Time, Compared
It’s worth separating two different claims that get conflated. First: relative to System V init, systemd is faster for a similar service workload, because it starts independent services in parallel instead of walking through numbered scripts one at a time. Second: relative to everything, nothing beats a stripped-down BusyBox init with a minimal boot script, because there’s no dependency resolution overhead to pay at all.
Worked Example: Picking An Init For A Real Board
Say you’re specifying init for an original board profile, ep-gateway-01: a network gateway with 16 MiB of NOR flash, five services (a modem manager, an MQTT bridge, a config web UI, an OTA updater, and a local log shipper) with real startup ordering requirements — the modem manager must be up before the MQTT bridge, and the config UI needs the modem manager’s IPC socket. It also needs to survive unattended for months in the field.
Board: ep-gateway-01
Flash budget: 16 MiB total, ~4 MiB reserved for init + support daemons
Services: 5, with real startup ordering dependencies
Field requirement: months of unattended uptime, needs watchdog protection
Walking the decision table: five services with genuine ordering dependencies rules out hand-rolled BusyBox inittab sequencing — you’d be reimplementing dependency logic in shell. The unattended uptime requirement plus the previous lecture’s watchdog needs point squarely at systemd. And 4 MiB is enough headroom for systemd’s ~10 MiB core components budget… except it isn’t — that’s the catch. On paper systemd fits the service-dependency and watchdog requirements perfectly, but the storage budget doesn’t have room for it. The resolution in a case like this is either negotiating more flash, or accepting the complexity cost of implementing dependency ordering and a lightweight watchdog feeder on top of BusyBox init. There is rarely a single “correct” answer — only the option that best fits the specific constraint that’s tightest for your product.
Common Mistakes
- Defaulting to systemd everywhere: pulling it in on a storage-constrained board without checking the actual footprint against your partition budget.
- Defaulting to BusyBox init everywhere: hand-rolling dependency ordering in shell scripts for a system that genuinely needs a dependency graph, instead of reaching for the tool built for that.
- Ignoring the Buildroot/Yocto default pull: fighting your build system’s natural default (Buildroot toward BusyBox init, Yocto often toward systemd) without a concrete reason usually costs more integration effort than it saves.
Best Practices
- Write down your actual constraints (storage budget, service count, dependency complexity, uptime requirement) before picking an init system — not after.
- Measure real boot time on your target hardware rather than trusting general claims from any lecture, including this one.
- If you’re on the edge between BusyBox init and systemd, prototype both against your real service set before committing.
Summary And Key Takeaways
- BusyBox init: smallest, fastest, best for static service sets and Buildroot-based systems.
- System V init: mainly a legacy/Yocto fallback with few standalone advantages today.
- systemd: best when you have real service dependencies, need watchdog/resource-control/logging features, and can afford roughly 10 MiB of storage for the core components.
- The right choice is always constraint-driven, not popularity-driven.
FAQ
Is systemd always the wrong choice for embedded boards?
No — it’s the right choice specifically when you have real service dependencies or need its watchdog and resource-control features, and your storage budget can absorb roughly 10 MiB for its core components.
Why does Buildroot lean toward BusyBox init by default?
Buildroot targets minimal, static embedded systems, which is exactly the profile BusyBox init was designed for, so it’s the natural default pairing.
Is System V init worth choosing for a new design?
Rarely on its own merits — it mainly persists as the fallback when a Yocto-based project specifically opts out of systemd.
Does systemd really boot faster than System V init?
For a similar service workload, yes, because it starts independent units in parallel rather than walking through numbered scripts sequentially.
What if my board is right on the edge of the systemd storage budget?
Prototype both BusyBox init and a minimal systemd build against your real service set and measure actual flash usage before deciding.
Can I mix approaches, like BusyBox init with a manual watchdog feeder?
Yes — that’s a common resolution when service-dependency and watchdog needs point to systemd but the storage budget doesn’t allow it.

2 Comments