1

I want to realize a scenario similar to the following:

Every day at XX:00 an umbrella service is started, e.g. daily_tasks.

When the service starts other services are started that can run in parallel, e.g. backup, cleanup, some_stuff

After all of these have completed there will be another service(s) run, e.g. dismount drives, shutdown.

Now I'm aware of partOf and that I can achieve the first two parts: The umbrella service and the child services. But I don't know how I can catch that all services are finished and kick off another service or another umbrella service. When I'm creating a simple empty umbrella service it has no knowledge of the child processes and will continue to run indefinitely.

I'd appreciate any ideas, even if the opinion is that systemd is not designed to handle this!

Thank you very much!

1 Answer 1

1

Create a target unit

First create a .target unit; this will act as our synchronization point. For this example, let's create daily-task.target:

[Unit]
Description=Run daily tasks

Create daily task services

For services that run in parallel, create Type=oneshot service units that (a) are RequiredBy=daily-task.target and (b) Run Before=daily-task.target. For example:

example1.service

[Unit]
Before=daily-task.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'sleep 10; date > /tmp/example1.txt'

[Install]
RequiredBy=daily-task.target

example2.service

[Unit]
Before=daily-task.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'sleep 15; date > /tmp/example2.txt'

[Install]
RequiredBy=daily-task.target

Create post-task services

For services that should run after everything is complete, create service units that (a) are RequiredBy=daily-task.target, and that (b) run After=daily-task.target. For example:

example3.service:

[Unit]
Description=Run after all daily tasks are complete
After=daily-task.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'sleep 10; date > /tmp/example3.txt'
ExecStopPost=/sbin/systemctl --no-block --user stop daily-tasks.target

[Install]
RequiredBy=daily-task.target

We run systemctl stop daily-task.target in this task (via an ExecStopPost hook) in order to move the target into a "stopped" state; if we don't do this, then next time we try to systemctl start daily-task.target nothing will happen, because the target is already started.

Enable your services

systemctl --user enable example{1,2,3}.service

Start the target

To run our daily tasks, we start the target:

systemctl --user start daily-tasks.target

If you are watching your logs when this happens, you will see:

Sep 06 17:44:19 hostname systemd[5435]: Starting example1.service...
Sep 06 17:44:19 hostname systemd[5435]: Starting example2.service...
Sep 06 17:44:29 hostname systemd[5435]: Finished example1.service.
Sep 06 17:44:34 hostname systemd[5435]: Finished example2.service.
Sep 06 17:44:34 hostname systemd[5435]: Reached target Start daily tasks.
Sep 06 17:44:34 hostname systemd[5435]: Starting example3.service...
Sep 06 17:44:44 hostname systemd[5435]: Stopped target Start daily tasks.
Sep 06 17:44:44 hostname systemd[5435]: Finished example3.service.

Due to the dependencies we have created combined with our use of Type=oneshot services, example1 and example2 run before daily-tasks.target is considered "started", and example3 runs once everything else is complete.

2
  • Wow thank you so much, this is exactly what I was looking for. I've read about targets before as umbrella for multiple services, but then I also read on multiple doc pages things like "systemd targets are different states that your system can boot into, comparable to System V runlevels.". That made me think that's not what I should use. Just one question regarding targets: Can a systemd timer trigger the start of a target?
    – DarkCell
    Commented Sep 7, 2022 at 7:20
  • Sure. You'll need to explicitly set Unit= in your [Timer] section.
    – larsks
    Commented Sep 7, 2022 at 11:20

You must log in to answer this question.

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