BuildNot yet confirmed elsewhere1 publisher2 min readPublished
PID 1 is close to unrepairable, and BusyBox init is the honest default
systemd earns its 5.10 kernel floor and glibc toolchain only when services need readiness ordering and resource limits. Plenty of fixed-function devices need neither.
The Engineer · Build desk
What happened
- A dev.to write-up on embedded init choice argues BusyBox init is the right default for products with a fixed service set and no user-installable software.
- It puts systemd's price in writing: a much larger image, a 5.10 kernel floor, a dependency set you maintain for the product's life, and a glibc toolchain under Buildroot.
- PID 1 fixes how services declare dependencies, how failures are detected and which libraries stay in the image, and it is among the hardest early choices to undo.
- The common pattern is inheriting init from the vendor BSP with no record of why, then working around it for two years.
Why it matters
- cost On an A/B device the systemd image growth is bought twice, so the flash line in the bill of materials absorbs double the delta before any feature ships.
- constraint Staying on BusyBox init leaves libc and kernel version as choices you can still make later; taking systemd spends both for the whole system at once.
- decision For a device that runs one process and reboots when it dies, the decision has not arrived yet, and buying the platform floor early commits to a topology that does not exist.
- exposure Respawn-only supervision pushes two failure classes onto support: the ordering race nobody saw until a customer unit failed, and the service that hangs without exiting.
The only brake on a crash loop under BusyBox init is the one-second sleep in its main loop, and upstream's own `init/init.c` documentation says as much: unlike sysvinit, it does not stop processes from respawning out of control [14]. So before treating a `respawn` entry as your supervision story [13], price the loop. A binary that exits immediately draws roughly 3,600 restart attempts an hour and about 86,400 a day [19], each one writing whatever it writes on the way down. That is the argument for the `syslogd` applet's `-C[size_kb]` option, which keeps the log in a shared-memory circular buffer that `logread` reads, and which the dev.to write-up calls the policy you usually want on a flash-based device [16].
BusyBox also ships a `watchdog` applet, with `-T N` to reboot after N seconds without a reset (60 by default), `-t N` for the reset interval and `-F` for foreground, and it runs from `inittab` as a `respawn` entry [15]. What that detects is a box that stopped petting the watchdog, not a unit that stopped answering. Of the three capabilities the piece credits systemd with earning its price [2], one is therefore partly covered by a binary already in the image, which leaves readiness ordering and resource limits as the actual purchase [20].
The write-up's four forces are a usable checklist for that purchase: image budget from the bill of materials, service topology (whether any service can fail without exiting), the platform floor of C library and kernel version, and the maintenance horizon, meaning how long you must ship security updates [6].
The avoidable error is buying PID 1 to get `/dev`. Buildroot keeps init and device management in separate menus and hides the `/dev` choice only when `BR2_INIT_SYSTEMD` is selected, so real udev rules for a USB modem or a camera are not a reason to take systemd with them [8]. Its general-purpose init list is longer than most summaries admit: `BR2_INIT_BUSYBOX` as the default, plus SysV, OpenRC and systemd, alongside four special-purpose options that are mostly container reapers [10]. On the Yocto side, `INIT_MANAGER` offers `sysvinit` (the Poky default, with udev), `mdev-busybox` and `systemd` [9].
What makes the BusyBox recommendation hold is the size of the thing you have to understand. `/etc/inittab` entries carry one of eight actions, from `sysinit` to `shutdown`, and the runlevel field is not even used [11]. Buildroot's default file mounts a few filesystems, runs `/etc/init.d/rcS` and starts a getty [12]. That is the entire boot in one readable file, which is the cheapest review any embedded team gets.
What to watch
- Whether Buildroot keeps the /dev management menu coupled to BR2_INIT_SYSTEMD in future releases, since that coupling is the reason teams think udev requires systemd.
- Any BusyBox change adding a start limit or restart back-off to init, which would remove the strongest technical objection to respawn-only supervision.
- Whether systemd's kernel floor moves above 5.10, which would re-price the platform-floor force for anyone tied to a vendor kernel.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence66
- Adoption
- Insufficient
- Hype gap+6
- Incentives30
- Confidence54
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
For most embedded products with a fixed set of services and no user-installable software, BusyBox init is the right default: small, no dependency chain, and its behaviour fits in one readable file.
- [2]
Choose systemd when the device genuinely needs supervised, interdependent services (readiness ordering, watchdog-backed liveness detection, resource limits), and accept in return a much larger image, a kernel floor of 5.10, a dependency set maintained for the life of the product, and, if building with Buildroot, a glibc toolchain for the whole system.
- [3]
The systemd vs BusyBox init choice is one of the first structural decisions in an embedded Linux product and one of the hardest to reverse: PID 1 defines how services declare dependencies, how failures are detected, and which libraries stay in the image forever.
- [4]
Many teams inherit the init choice from the vendor BSP without recording why, then work around it for two years; the problem is the unrecorded decision, not the inherited default.
- [5]
The decision only arises once a product moves past a single application binary: a device that boots, runs one process and reboots on failure needs nothing beyond a supervising parent. The forces appear when the product gains a second and third long-running service with an ordering relationship.
- [6]
Four forces decide the choice: the image budget from the bill of materials, doubled in an A/B layout; the service topology, meaning whether any service can fail without exiting; the platform floor, meaning the C library and kernel version committed to; and the maintenance horizon, meaning how long security updates must ship.
- [7]
Init and device management are two decisions, not one. Yocto's INIT_MANAGER values bundle them, but the device manager has its own variable, VIRTUAL-RUNTIME_dev_manager, with documented values udev, busybox-mdev and systemd.
- [8]
Buildroot keeps init and /dev management in separate menus, and its /dev management choice is hidden only when BR2_INIT_SYSTEMD is selected; if you need real udev rules for a USB modem or a camera, you do not have to take systemd to get them.
- [9]
Yocto INIT_MANAGER values include sysvinit (the Poky default, SysVinit plus udev), mdev-busybox (BusyBox init plus BusyBox mdev) and systemd (systemd plus udev).
- [10]
Buildroot's System configuration / Init system menu has four general-purpose options, BR2_INIT_BUSYBOX (the default), BR2_INIT_SYSV, BR2_INIT_OPENRC and BR2_INIT_SYSTEMD, plus four special-purpose ones that are mostly container reapers.
- [11]
BusyBox init reads /etc/inittab at startup; entries take the form <id>:<runlevels>:<action>:<process>, with the action one of sysinit, wait, once, respawn, askfirst, restart, ctrlaltdel or shutdown. The runlevel field is unused.
- [12]
Buildroot's default inittab mounts a few filesystems, runs /etc/init.d/rcS and starts a getty.
- [13]
BusyBox init supervision in its simplest useful form: an inittab entry marked respawn is restarted when it exits.
- [14]
BusyBox's own documentation in init/init.c states: "Unlike sysvinit, BusyBox init does not stop processes from respawning out of control." There is no back-off and no start limit: a process that exits immediately is restarted again and again, paced only by the one-second sleep in init's main loop.
- [15]
The BusyBox watchdog applet takes -T N (reboot after N seconds if not reset, default 60), -t N (reset every N seconds) and -F (foreground), so it can be run from inittab as a respawn entry.
- [16]
The BusyBox syslogd applet accepts -C[size_kb] to log to a shared-memory circular buffer that logread reads; on a flash-based device this is often exactly the logging policy you want.
- [17]
In BusyBox init's favour: negligible size on top of BusyBox, no new libraries, a boot sequence one engineer can read in full before changing it, no C library or kernel constraint beyond BusyBox's own, and failure behaviour that is exactly what you wrote.
- [18]
Against BusyBox init: ordering is whatever order you wrote the script in, so a race between two services can remain undetected until a unit fails at a customer site; a hung service stays hung; there is no restart back-off; and supervision beyond respawn is yours to write, test and document.
- [19]
A process that exits immediately under BusyBox init draws about 3,600 restart attempts per hour and about 86,400 per day, since the one-second sleep in init's main loop is the only pacing.
- [20]
Of the three capabilities cited as justifying systemd, the BusyBox watchdog applet covers box-level liveness (reboot on a missed reset) but not per-service hangs, leaving readiness ordering and resource limits as the two capabilities systemd actually adds.
- [21]
Because the image budget is doubled in an A/B layout, systemd's image growth is charged twice against the flash budget on an A/B device.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.tosystemd vs BusyBox init: Which Init System Fits Your Device?
1 article · August 25, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.