So far in this free linux kernel development course, every init example has been built on System V init and BusyBox. That covers most small embedded devices, but as products grow more complex — more services, more dependencies, tighter security requirements — many teams reach for systemd instead. This lecture introduces the three concepts you need before touching a single unit file: targets, services, and units, plus how to actually enable systemd in a Yocto or Buildroot build.
What You Will Learn
- Why systemd exists and what problems it solves compared to System V init
- The three core concepts: targets, services, and units
- How to enable systemd as the init manager in Yocto
- How to enable systemd as the init option in Buildroot
- Where systemctl fits into daily embedded Linux workflow
Prerequisites
You should already be comfortable with what an init system’s job is (covered earlier in this series) and ideally have followed along with the System V init.d lecture, since most of the comparisons here are relative to that model. A Yocto or Buildroot based build environment is useful if you want to try building a systemd-enabled image yourself, but is not required to follow the concepts.
Why systemd for Embedded Linux
systemd describes itself as a system and service manager, and its scope goes well beyond replacing /etc/init.d — it also absorbs device management (via udev) and system logging into an integrated whole. For a desktop distribution, that integration is mostly about a smoother developer experience. For an embedded product, several of systemd’s properties matter directly:
| System V init | systemd |
|---|---|
| Ordering via two-digit numeric prefixes on symlinks | Explicit declared dependencies between services |
| Shell scripts implementing start/stop by hand | Declarative unit files describing what to run and how |
| No built-in service supervision | Can monitor a service and restart it automatically on failure |
| No standard resource limiting per service | Per-unit permission and resource limit controls, useful for security hardening |
| Services generally started serially | Independent services started in parallel, which can shorten boot time |
| No standard watchdog integration | Built-in watchdog support for individual services and for systemd itself |
None of this makes System V init wrong for every embedded project — a very small, resource-constrained device with a handful of services often has no real need for systemd’s overhead. But on more complex boards running several interdependent services, these properties add up to meaningfully less boilerplate and more resilience.
The Three Core Concepts
Before writing or reading a single systemd configuration file, you need these three ideas straight, because the terminology is used constantly and inconsistently by people who conflate them.
- Target — a group of services, conceptually similar to a System V runlevel but more general: targets can pull in other targets, and a service can belong to more than one target. There is always a default target, which is the set of units systemd brings up automatically at boot.
- Service — a daemon that can be started and stopped, directly comparable to a System V service, except its behavior is described declaratively instead of imperatively.
- Unit — the actual configuration file itself. A unit is a plain text file made of key/value properties, and it can describe a service, a target, or several other unit types (mounts, timers, sockets, and more) that don’t have a System V equivalent at all.
A minimal service unit, purely for illustration of the shape (we’ll write full unit files with dependency ordering in the next lecture), looks like this:
[Unit]
Description=EP Datalogger Service
[Service]
ExecStart=/usr/bin/ep_datalogger
Restart=on-failure
[Install]
WantedBy=multi-user.target
Compare this to the equivalent System V script from the previous lecture: there is no shell logic, no manual backgrounding, and the restart-on-failure behavior — something you’d have to build yourself with a watchdog wrapper under System V — is a single declarative line.
Enabling systemd in Yocto
Yocto-based distributions default to System V init. To switch the init manager to systemd, add the following to your conf/local.conf (or better, a distro configuration file):
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES:append concatenates strings directly, so a missing leading space before systemd silently merges it with the previous feature name instead of adding a new one.
After setting these, a full rebuild is required — this affects which init-related recipes and packages get pulled into the image, not just runtime configuration.
Enabling systemd in Buildroot
Buildroot offers systemd as a third init option alongside BusyBox init and System V init. Selecting it in menuconfig under the init system option brings in systemd as the PID 1 process. Two things are worth checking before you flip the switch on an existing project:
- systemd requires glibc as the C library — it is not compatible with uClibc-ng or musl builds
- It requires a reasonably modern kernel with a specific set of configuration options enabled (cgroups, inotify, and several others), and a documented minimum kernel version
The authoritative, currently-maintained list of these kernel dependencies ships in the systemd source tree’s own README, and it’s worth checking against your target kernel version rather than relying on older tutorials, since the minimum requirements have crept up over systemd’s development.
Where systemctl Fits In
Once systemd is your PID 1, the systemctl command becomes your day-to-day interface for everything: starting and stopping services, inspecting why something failed, switching targets, and enabling units to run automatically at the next boot. We’ll spend a full lecture on systemctl and unit-file authoring next — for now, the key mental model is: System V’s “call a script by hand” becomes systemd’s “ask systemctl to change a unit’s state,” and systemd itself works out the correct order from the declared dependencies.
Real-World Use Cases
- Multi-service gateways where a database, an application, and a network agent all depend on each other in a specific order that’s painful to express as numeric prefixes
- Products needing automatic service recovery in the field, without a custom supervisor daemon
- Security-hardened devices that need per-service sandboxing (restricted filesystem access, capability dropping) expressed declaratively
- Boards where boot time matters and parallel service startup measurably shortens time-to-ready
Common Mistakes and Troubleshooting
Best Practices
- Decide between System V/BusyBox init and systemd early in a project — retrofitting later means rewriting all your service definitions
- Use declared dependencies (
After=,Requires=,Wants=) instead of trying to fake ordering with unrelated tricks - Turn on
Restart=on-failurefor services that must self-heal, rather than building a bespoke supervisor - Keep unit files under version control alongside your application code, the same way you would an init.d script
Performance Considerations
Parallel service startup is systemd’s headline boot-time advantage, but it isn’t automatic for every service — units only start in parallel when their declared dependencies allow it. A service with an overly conservative After= chain gains nothing from systemd’s parallelism, so it’s worth reviewing dependency declarations specifically with boot time in mind on time-sensitive products.
Security Considerations
Per-unit resource and permission controls are one of systemd’s strongest embedded-relevant features: options like restricting filesystem namespaces, dropping capabilities, and limiting memory or CPU per service let you sandbox each daemon individually, something that required significant custom tooling under System V init.
Summary and Key Takeaways
- systemd is a system and service manager that also absorbs device management and logging
- Targets group services (like a more general runlevel), services are daemons, and units are the configuration files describing both
- Enabling systemd requires build-system changes — a
DISTRO_FEATURESline in Yocto, or a menuconfig selection in Buildroot — plus, for Buildroot, glibc and specific kernel options - systemctl is the primary day-to-day interface once systemd is PID 1, replacing direct init.d script invocation
This introduction sets up everything you need before writing real unit files. In the next lecture of this free linux kernel development course, we’ll author complete service, target, and timer units and walk through systemctl commands in detail on a running board.
Frequently Asked Questions
Is systemd better than System V init for every embedded device?
No. Very small, resource-constrained devices with only a handful of services often do fine with BusyBox/System V init and gain little from systemd’s overhead. systemd shines as service count and interdependency grow.
What is a systemd target, in one sentence?
A named group of units, conceptually similar to a runlevel but more flexible, since targets can include other targets and a unit can belong to multiple targets.
Can I mix systemd and BusyBox init scripts?
Not directly — once systemd is PID 1, services need to be described as unit files. BusyBox’s own init applet is a different init implementation and isn’t used alongside systemd.
Does Buildroot support systemd out of the box?
Yes, systemd is one of the three init options available in Buildroot’s menuconfig, alongside BusyBox init and System V init, but it requires glibc and a compatible kernel configuration.
What’s the minimum kernel version for systemd?
This has changed over time as systemd has evolved; always check the README shipped in the systemd source tree for the currently required minimum version and kernel config options rather than relying on an old figure.
Why would parallel startup not speed up my boot?
Units only run in parallel when their declared dependencies allow it. An overly cautious dependency chain (excessive After= declarations) effectively serializes startup even under systemd.
Keep Learning with EmbeddedPathashala
This free linux kernel development course continues with hands-on systemd unit authoring and systemctl workflows.
Browse the Course Index Next Lecture
2 Comments