29

I cannot find the correct way to execute some local scripts (or very local commands) at systemd, I already know I must not create a service (in systemd a unit) for this kinds of scripts (or I must?)....

The workaround that I found is to create rc.local and give to it execution permissions.

printf '#!/bin/bash \n\nexit 0' >/etc/rc.local 
chmod +x /etc/rc.local

For example, if I get a legacy server with a simple rc.local configured by you, I will know what did you do and how much it gonna hurt to upgrade or install something new on the distro, as rc.local was respected by external packages, but in the other hand if I install a server and create a systemd unit or two or three (or even sysvinit services), just for doing a simple task, this can sometimes make your life harder, and much more than this my units names can someday conflicts with the names of the new services created by the distribution development, and maybe installed on a upgrade, causing trouble for my scripts !

I see another question asking about where is rc.local and the answer was to create it and give execution permissions, I think my question is really not a duplicate, because I do not want to know where it is - believe me, I just want to accept that it is deprecated, but i cannot find the correct way for doing this kind of things, should I really create a unit just for some simple like that?

  • 4
    systemd "the only winning move is not to play"; alternatively, depending on what you want, you may create a systemd Unit. I would probably use rc.local for now, and care about it when it goes away. – Rui F Ribeiro Sep 27 '18 at 13:13
  • So you think the official way is to create a unit like a service? Please post as a answer how to do that. – Luciano Andress Martini Sep 27 '18 at 13:18
  • 1
    When I tried Ubuntu I created an Unit for having a persistent ssh-agent caching in my desktop while I did not reboot and another for something else I cannot remember; you can also create one pointing to /etc/rc.local but it seems the system is doing it for yourself for now....I would rc.local for the time being, and if is discontinued, create one pointing to a fake /etc/rc.local then. – Rui F Ribeiro Sep 27 '18 at 13:20
  • This is likely to break documentations as if some book say that you must put something in /etc/rc.local, and you try to upgrade this docs, saying for example that rc.local must be marked as executable, when it becomes totally deprecated the documentation is broken, but if you say that you must create unit to point to /etc/rc.local, this brokes documentation because systemd is conflicting with your unit. I believe if you are right, they probably did not decided if it is deprecated or not, because they still don't have a substitute... when they decide all documentation will be broken again. – Luciano Andress Martini Sep 27 '18 at 13:33
  • Which kind of script do you need to execute? Systemd has a lot of unit types. "Service" is only one of many unit types. E.g. Mount units, automount units, timer units, target units. Could one of them solve your problem? – andcoz Sep 27 '18 at 14:48
19

As pointed out elsewhere, it becomes moderately unclean to use rc-local.service under systemd.

  1. It is theoretically possible that your distribution will not enable it. (I think this is not common, e.g. because disabling the same build option also removes poweroff / reboot commands that a lot of people use).
  2. The semantics are not entirely clear. Systemd defines rc-local.service one way, but Debian provides a drop-in file which alters at least one important setting.

rc-local.service can often work well. If you're worried about the above, all you need to do is make your own copy of it! Here's the magic:

# /etc/systemd/system/my-startup.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/libexec/my-startup-script

[Install]
WantedBy=multi-user.target

I don't think you need to understand every single detail[*], but there are two things you need to know here.

  1. You need to enable this with systemctl enable my-startup.service.

  2. If your script has a dependency on any other service, including network-online.target, you must declare it. E.g. add a [Unit] section, with the lines Wants=network-online.target and After=network-online.target.

    You don't need to worry about dependencies on "early boot" services - specifically, services that are already ordered before basic.target. Services like my-startup.service are automatically ordered after basic.target, unless they set DefaultDependencies=no.

    If you're not sure whether one of your dependencies is an "early boot" service, one approach is to list the services that are ordered before basic.target, by running systemctl list-dependencies --after basic.target. (Note that's --after, not --before).

There are some considerations that I think also applied to pre-systemd rc.local:

  1. You need to make sure your commands are not conflicting with another program that tries to control the same thing.
  2. It is best not to start long-running programs aka daemons from rc.local.

[*] I used Type=oneshot + RemainAfterExit=yes because it makes more sense for most one-shot scripts. It formalizes that you will run a series of commands, that my-startup will be shown as "active" once they have completed, and that you will not start a daemon.

|improve this answer|||||
  • Well, there is some kind of "local" place for creating services or snippets, or whatever - or can i trust this is local and should not be altered? if i develop my own daemons? Because i don't want to alter the originallity of the system, so when i install some apt-get i dont want to see my system on fire because of a replaced script, or something like this for example. I just want to trust the daemon will be just started only once, without doing any additional crazy things that i fear... like trying to restart it as infinite if it is broken, etc... – Luciano Andress Martini Nov 5 '18 at 19:05
  • 1
    @LucianoAndressMartini if you're asking what the right way to start a daemon is, you should really define an individual service for it. That can be a native system .service unit, or if you really want to write an LSB init script you can do that instead. I didn't want to recommend putting several daemons in rc-local.service or equivalent, I think it probably works, but it seems much nicer if you can restart the individual daemon with systemctl restart my-daemon just like anything else. It's intended that you put your local services in /etc/systemd/system. – sourcejedi Nov 5 '18 at 21:45
  • 1
    @LucianoAndressMartini you have to avoid using the same name as any other service - just like in sysvinit. In similar situations I've sometimes started my names with local-... – sourcejedi Nov 5 '18 at 21:49
  • 1
    Thank you!!! Saved after half a day. These details were critical for me: Type=oneshot, RemainAfterExit=yes, Wants=network-online.target, After=network-online.target. Just deploying a build of apache and setting static IP to 192.168.1.80, so the router can direct traffic there. Should be trivial. It's annoying in Debian9. Hardly any online advice except "apt-get apache", "systemctl start apache" or instructions with init.d, none of which apply for source-built Apache under Debian9. – MichaelsonBritt Jun 4 '19 at 1:26
  • 1
    @MichaelsonBritt It is best not to start long-running programs aka daemons from rc.local. I used Type=oneshot ... it formalizes that ... you will not start a daemon. If you want information about starting a daemon, please ask a new question. – sourcejedi Jun 8 '19 at 17:54
15

Forget about rc.local.

As I said about CentOS 7 and about Debian 8 and about Ubuntu 15:

You're using a systemd+Linux operating system. /etc/rc.local is a double backwards compatibility mechanism in systemd, because it is a backwards compatibility mechanism for a mechanism that was itself a compatibility mechanism in the van Smoorenburg System 5 rc clone.

Using /etc/rc.local can go horribly wrong. People have been surprised by the fact that systemd doesn't run rc.local in quite the same way, in quite the same place in the bootstrap, as they are used to. (Or erroneously expect: It did not, in fact, run last in the old system, as the OpenBSD manual still points out.) Others have been surprised by the fact that what they set up in rc.local expecting the old ways of doing things, is then completely undone by the likes of new udev rules, NetworkManager, systemd-logind, systemd-resolved, or various "Kit"s.

As exemplified by "Why does `init 0` result in "Excess Arguments" on Arch install?", some operating systems already provide systemd without the backwards compatibility features such as the systemd-rc-local-generator generator. Whilst Debian still retains the backwards compatibility features, Arch Linux builds systemd with them turned off. So on Arch and operating systems like it expect /etc/rc.local to be entirely ignored.

Forget about rc.local. It's not the way to go. You have a systemd+Linux operating system. So make a proper systemd service unit, and don't begin from a point that is two levels of backwards compatibility away. (On Ubuntu and Fedora, it is three times removed, the van Smoorenburg System 5 rc clone that followed rc.local having then been itself twice superseded, over a decade ago, first by upstart and then by systemd.)

Also remember the first rule for migrating to systemd.

This is not even a new idea that is specific to systemd. On van Smoorenburg rc and Upstart systems, the thing to do was to make a proper van Smoorenburg rc script or Upstart job file rather than use rc.local. Even FreeBSD's manual notes that nowadays one creates a proper Mewburn rc script instead of using /etc/rc.local. Mewburn rc was introduced by NetBSD 1.5 in 2000.

/etc/rc.local dates from the time of Seventh Edition Unix and before. It was superseded by /etc/inittab and a runlevel-based rc in AT&T Unix System 3 (with a slightly different /etc/inittab in AT&T Unix System 5) in 1983. Even that is now history.

Create proper native service definitions for your service management system, whether that be a service bundle for the nosh toolset's service-manager and system-control, an /etc/rc.d/ script for Mewburn rc, a service unit file for systemd, a job file for Upstart, a service directory for runit/s6/daemontools-encore, or even an /etc/init.d/ script for van Smoorenburg rc.

In systemd, such administrator-added service unit files go in /etc/systemd/system/ usually (or /usr/local/lib/systemd/system/ rarely). With the nosh service manager, /var/local/sv/ is a conventional place for local service bundles. Mewburn rc on FreeBSD uses /usr/local/etc/rc.d/. Packaged service unit files and service bundles, if you are making them, go in different places, though.

Further reading

|improve this answer|||||
  • 2
    @LucianoAndressMartini A better question would be how to handle a specific snippet of rc.local in a systemd service. So if you have a specific snippet in mind (you mention instructions from some software's documentation), maybe post a question about that one instead? There are certain general rules that can be used for conversion, but you get more out of it by exploiting features of the software you're running (such as running in foreground, not trying to daemonize, etc.) so asking about a specific case might be helpful. – filbranden Sep 27 '18 at 19:54
  • 5
    You gotta wonder if it has survived deprecation since 1983, maybe it has something going for it? – dan carter Aug 9 '19 at 8:24
  • 4
    This answer is preachy and fails to actually just answer the simple "what trivial thing should I do instead" question. It may contain information, and provide tons of references, but defeats the purpose of stackexchange. – gps Aug 19 '19 at 23:19
  • 1
    I bring this (the end of rc.local) up, together with dns resolving issues, as reasons why linux isn't evolving, but actually getting to be spaghetti-code more and more, systemd has become a black box for many. If a user or administrator wants to put something in rc.local and no longer can, it's not helpful to force them to first do courses systemd or whatever else you preach in order to achieve the same end-result in days, instead of minutes. Of course, idiots can do stupid things with everything that works well and is easy to work with, that's never a valid argument to end it. – Julius Sep 18 '19 at 12:54
2

Summary of https://www.linuxbabe.com/linux-server/how-to-enable-etcrc-local-with-systemd

Create /etc/systemd/system/rc-local.service:

# /etc/systemd/system/rc-local.service
[Unit]
 Description=/etc/rc.local Compatibility
 ConditionPathExists=/etc/rc.local

[Service]
 Type=forking
 ExecStart=/etc/rc.local start
 TimeoutSec=0
 StandardOutput=tty
 RemainAfterExit=yes
 SysVStartPriority=99

[Install]
 WantedBy=multi-user.target

Then:

sudo touch /etc/rc.local
sudo chmod +x /etc/rc.local
sudo systemctl enable rc-local

Check with:

sudo systemctl start rc-local.service
sudo systemctl status rc-local.service
|improve this answer|||||
  • The systemd-provided one uses StandardOutput=journal+console (console is equivalent to tty in your example), which seems like it would be much more useful for troubleshooting. Also support for SysVStartPriority= has been removed at some point. Maybe you could comment how important SysVStartPriority= is, or remove it. Apart from that, it's a decent answer. – sourcejedi Nov 4 '19 at 13:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.