How to Install a systemd Service in a Yocto Recipe
A Yocto systemd recipe needs three aligned pieces: (1) inherit systemd plus SYSTEMD_SERVICE in the recipe, (2) install the unit under ${D}${systemd_system_unitdir} (documented equivalently as ${D}${systemd_unitdir}/system), and (3) turn on systemd as the init manager for the distro/image. This is a minimal howto from the official systemd class and init-manager docs—no vendor layers, no pricing.
Which inherit?
One-line answer: In the recipe that packages the unit, inherit systemd and set SYSTEMD_SERVICE:${PN} to the service file name. The class is active only when systemd is in DISTRO_FEATURES.
From the Yocto Project Reference Manual systemd class:
inherit systemdSYSTEMD_SERVICE:${PN} = "mydaemon.service"(space-separated for multiple)- Services auto-enable on boot unless
SYSTEMD_AUTO_ENABLE:${PN} = "disable" - If units live outside the main package, set
SYSTEMD_PACKAGES
Minimal skeleton:
SUMMARY = "Example daemon with systemd unit"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://mydaemon.service"
inherit systemd
SYSTEMD_SERVICE:${PN} = "mydaemon.service"
do_install() {
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/mydaemon.service ${D}${systemd_system_unitdir}/
}
OE examples such as connman use SYSTEMD_SERVICE:${PN} = "connman.service". Prefer the : override form over legacy SYSTEMD_SERVICE_${PN} on current releases.
Where do the files go?
One-line answer: Install units under ${D}${systemd_system_unitdir} in do_install. The docs also describe ${D}${systemd_unitdir}/system. The class expects those paths when it fills FILES, presets, and package scriptlets.
Practical checks:
- List
file://foo.serviceinSRC_URIand install fromWORKDIR. - A wrong path fails packaging with
Didn't find service unit '…'(systemd.bbclasssearch). - Template units (
foo@.service) must matchSYSTEMD_SERVICEnames; instance enable follows class preset rules. - Most board daemons are system units; user units use separate search paths—check the class if you need them.
Example unit:
[Unit]
Description: Example daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/mydaemon
Restart=on-failure
[Install]
WantedBy=multi-user.target
Keep ExecStart aligned with where the recipe installs the binary (bindir).
Which IMAGE feature flag?
One-line answer: systemd is selected mainly via INIT_MANAGER = "systemd" (distro/local.conf), not a typical IMAGE_FEATURES toggle. inherit systemd in a recipe does not by itself make the image boot with systemd.
Per Selecting an Initialization Manager:
INIT_MANAGER = "systemd"
That pulls the systemd init-manager include and drops competing sysvinit pieces. Older write-ups often show the manual axis:
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
| Goal | Where | What |
|---|---|---|
| Package units | recipe | inherit systemd, SYSTEMD_SERVICE |
| Unit path | do_install | ${D}${systemd_system_unitdir} |
| Image init | distro / local.conf | INIT_MANAGER = "systemd" |
| Class enabled | DISTRO_FEATURES | needs systemd |
Also ensure the app package is in the image (IMAGE_INSTALL / packagegroup). systemd init without the package means no unit on rootfs.
Wrap-up
A Yocto systemd recipe is inherit systemd + SYSTEMD_SERVICE:${PN} + install into the system unitdir; the image side picks systemd with INIT_MANAGER = "systemd". See the systemd class and Initialization Manager.