49

Many years ago, we can write our startup-script into /etc/rc.local. After all system services loaded, your script will run.

Now, we use systemd, we don't have rc.local anymore. Systemd starts the service parallel. You can write your own service to act a rc.local` but you can't ensure it will run after all system services loaded.

Is there a way to do it? Or we have to use Before and After in the systemd service file?

11
  • upstart.ubuntu.com/getting-started.html
    – STTR
    Jan 31, 2013 at 9:33
  • 7
    Systemd, not Upstart!! Jan 31, 2013 at 9:46
  • Specify name and version OS?
    – STTR
    Jan 31, 2013 at 9:59
  • OS: Arch Linux, Version: N/A Jan 31, 2013 at 10:02
  • 1
    @比尔盖子 Why "last", do you not know the dependencies of the script, or do you want it last just to be safe?
    – Paul
    Jan 31, 2013 at 13:05

3 Answers 3

44

In systemd it is advised to use Before= and After= to order your services nicely around the other ones.

But since you asked for a way without using Before and After, you can use:

Type=idle

which as man systemd.service explains:

Behavior of idle is very similar to simple; however, actual execution of the service program is delayed until all active jobs are dispatched. This may be used to avoid interleaving of output of shell services with the status output on the console. Note that this type is useful only to improve console output, it is not useful as a general unit ordering tool, and the effect of this service type is subject to a 5s timeout, after which the service program is invoked anyway.

4
  • 2
    Hi, would you care to elaborate the syntax if I was gonna use before or after?
    – r4ccoon
    Dec 28, 2015 at 21:43
  • Somewhat different topic, but if someone wants to run user processes separate from system processes, there's askubuntu.com/a/859583/457417
    – Ben Creasy
    Oct 8, 2017 at 5:25
  • worked perfectly to set /proc/sys/kernel/modules_disabled to 1 at the end of the boot process Jul 25, 2018 at 20:35
  • 3
    The quoted excerpt for the idle Type includes this: Note that this type is useful only to improve console output, it is not useful as a general unit ordering tool, and the effect of this service type is subject to a 5s timeout, after which the service program is invoked anyway. Doesn't that mean, effectively, that processes set as idle will only be delayed by 5 secs, and further that idle will not necessarily accomplish the OP's stated objective: ... run after all system services loaded.??
    – Seamus
    Nov 29, 2021 at 0:41
10

The best way to make sure our service will execute after all other enabled services is to create your own target and make it run after multi-user.target.

Typically:

  1. Create a target unit /etc/systemd/system/custom.target file with AllowIsolate=yes
[Unit]
Description=My Custom Target
Requires=multi-user.target
After=multi-user.target
AllowIsolate=yes
  1. Create you service unit file /etc/systemd/system/last_command.service with After=multi-user.target and WantedBy=custom.target
[Unit]
Description=My custom command
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/local/bin/my_last_command.sh

[Install]
WantedBy=custom.target
  1. Create the /etc/systemd/system/custom.target.wants directory
  2. Link your last_command.service into /etc/systemd/system/custom.target.wants
ln -s /etc/systemd/system/last_command.service \
  /etc/systemd/system/custom.target.wants/last_command.service
  1. Reload systemd with systemctl daemon-reload
  2. Set the system default target as custom.target
systemctl set-default custom.target
  1. Optionally, you may want apply immediately the custom.target
systemctl isolate custom.target

This way, for each reboot, your last_command service will be executed after the multi-user.target is reached.

3
  • 1
    Does this mean that specifying After=multi-user.target in a unit will have the same effect?
    – Hosh Sadiq
    Jan 22, 2023 at 21:38
  • 1
    @HoshSadiq Not necessarily, After= tells "this service should start after all services wanted and required by Multi-user target" but it tells nothing about a situation where Multi-user.target is not started for any reason. That is why custom.target also has the Requires=multi-user.target directive and why custom.target is set to the default target. This way by calling custom.target the system calls multi-user.target. More in the doc
    – MCO System
    Jan 24, 2023 at 8:40
  • You can also just create this target under multi-user.target directory and control the Before= and After= in there. If messing with mount points often you need nofail to avoid other services preventing your ordering.
    – dza
    Jun 5, 2023 at 22:29
4

It really depends on your definition of "booted". I assume you want it to run immediately after the getty starts. To do this, you need to add your service to /etc/systemd/system/getty.target.wants/ directory. You should also ensure your file is using similar code to the other services in this directory. To run a custom service on bootup and shutdown (just beeps my motherboard buzzer) I use the following script in /etc/systemd/system/getty.target.wants/service_name.service

[Unit]
After=systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service
Before=getty.target
IgnoreOnIsolate=yes

[Service]
ExecStart=/usr/bin/myinitscript.sh start
ExecStop=/usr/bin/myinitscript.sh stop
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=basic.target

/usr/bin/myinitscript.sh is executable and has a shebang at the start.

Note that not everything will be started at this point in the boot, but this is the point at which the logon prompt appears to the user

Although this does use Before= and After=, it was for me much more understandable and actually works; I didn't find the above answer informative enough. This is also allows you to use both ExecStart= and ExecStop=, rather than being limited to a Type=simple-like service.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .