[This does not directly address the issue of systemd-tmpfiles but I think you have already recognized that in this particular case you are better off just using echo.]
First up, "multi-user.target" may or may not be what you want to use. If you are familiar with the concept of runlevels from SysV style init stuff, multi-user is the systemd equivalent of runlevel 3, which is a multi-user system that boots to a console, not a GUI. The equivalent of runlevel 5, which boots to X, is graphical.target. The default is determined by a symlink in
/lib/systemd/system; the one in
/etc will overrule the one in
/lib) called default.target, use ls to find where it points:
»ls -l /etc/systemd/system/default.target
default.target -> /usr/lib/systemd/system/multi-user.target
systemctl get-default will tell you "multi-user.target" in this case.
For normal linux desktops it will be graphical.target. This is actually not important if you want the boot service you are creating to start regardless of what the default runlevel/target is -- in that case, we can just use default.target, and not worry what it is an alias for. If you use multi-user, however, and your default is graphical, your service won't happen.
Depending on the service, there may be more appropriate and specific targets or services that you want to start this one in relation to. Based on your other question, default.target is probably fine. As a note, the difference between a "target" and a "service" is that a service contains a
[Service] section which actually runs a process; a target is just a way of grouping services together via the various "depends" and "requires" directives; it doesn't do anything of its own beyond triggering other targets or services.
When a service starts is determined by what other services explicitly depend on it. In the case of a simple, stand-alone event like this that we want run late in the boot process, we can use this combination of directives:
The "Install" section is used when the service is installed. "WantedBy=" specifies a target we want this service to be included with, meaning it will run if that target does. If you don't have specific dependencies, getting the unit to run later rather than sooner may be a matter of looking at what's going on normally and picking something to use as a dependency or an optional prerequisite.
To distinguish: By dependency I mean something which your unit requires to also be activated, and by optional prerequisite I mean something that should run before your unit if it is being used, but it is not required. Those terms are mine, but this is an important distinction used in the systemd documentation, particularly in the sense that a required dependency is guaranteed to be started if your unit is, but this requirement does not influence the order in which they are started, meaning, something that is just a dependency may actually be started afterwards (and yes, since that means your unit may be started first, the dependency is not guaranteed to succeed).
local-fs.target may be a bit pointless unless you think your unit is going to be used on a system where it might not be included otherwise, but combining it with
After means your unit is guaranteed to be started after it is -- so you could do without the
Requires (you can set a unit to start after a unit that it doesn't depend on, hence "After"
without "Requires" = an optional prerequisite). The example here is just to introduce the concepts and the distinction between dependency and order of execution: One does not determine the other.
Note that "started after" still doesn't mean the prereq will have reached any particular point it its own execution. Eg., if it is about mounting remote filesystems and you this is important to your unit, you will want to use
Requires and probably
After the service that establishes that but you still need the actual process you are executing to do proper error handling in case the remote filesystems are not yet accessible (eg., by sleeping in a loop until they are).
For the example, I'll just echo "hello world" to the console. The service itself is described in the
The command needs a full path. The reason I did not just use
/usr/bin/echo "hello world" is that it won't work (the output goes to /dev/null, I think), and while a service that does an
echo "hello world" > /dev/console will, experimentation demonstrates that using shell redirection in an ExecStart directive won't, because the
ExecStart command isn't run by a shell. But you can make so: /usr/local/bin/helloworld is a shell script with that one line,
echo "hello world" > /dev/console.1
Type=simple. This is fine for what
helloworld does, and a great many other things.
If your service is long running (beyond a few seconds), systemd will fork it to the background when using
simple, which is what you want (the other option is to have it killed for remaining in the foreground too long). However, if the program does this fork itself (as servers and daemons often do) you should use
simple, it will be killed as a stray orphan process. The "Type" param is covered in detail in
man systemd.service and you should read that part regardless of what you are trying to do.2
Our complete, minimal service file is just those three sections (
[Install]). To install, place the file or a symlink to it in either /etc/systemd/system or /usr/lib/systemd/system, and:
systemctl --system enable helloworld
It should print
ln -s .... This does not run the service, it just configures it to run at boot as discussed above.
That's it in a nutshell.
man systemd.unit and
man systemd.service have more details (BTW, there's an index for all these things in
You can redirect output using
StandardError parameters in the
[Service] block, see
There is an index of all service file directives in
man systemd-directives, indicating which man page there are documented in, eg.: