Use systemd to execute scripts, controlling the order of execution.
- Assuming we have:
- a single preparation unit - prep.service
- multiple "task" units - task-N.service
- a single cleanup script - done.service
We want to run the prep unit once, then run all our task units and finally the done unit once.
Defining dependencies between tasks is relatively simple when each task is represented by a systemd unit . Dependencies are defined using Requires= or Wants= and order of execution is defined with After= and Before=.
The only thing left is to make sure that the "preparation" and "finish" steps are executed only once, even if multiple tasks are run (and not once for each task).
To achieve this a .target is created and all tasks are associated to that target.
- Create .service files for the commands that must be executed before
- Create .service for commands that should be executed after
- Create as single .target for all tasks
- Create a .service for each task, make it WantedBy= the target.
The target file goes into /etc/systemd/system and looks like this:
The target can be started manually with:
The task units are associated with the target using WantedBy=.
This means, if we enable one or more task units with
... starting the tasks.target will start all of the associated units.
This allows us to start all tasks with a single invocation which in turn means that we execute any number of tasks and the prep and done units only once.
Each task unit specifies the prep.service as a precondition using Requires= and After=. It also specifies the done.service using Wants= and Before= to have it run after the tasks.
# task.service --------------------------------------- [Unit] Description = A task unit Requires = prep.service After = prep.service Wants = done.service Before = done.service [Service] User = someuser Group = somegroup Type = oneshot ExecStart = /usr/local/bin/script.sh [Install] WantedBy = tasks.target
The Requires=prep.service directive means that the task is not started if the prep.service failed. Using Wants=prep.service would try to start the prep unit first but continue with the tasks regardless of whether preparation was successful or not.
The User= and Group= properties can be used to run the command under a different user than root.
Not all tasks have to specify the same dependencies. It is also possible that tasks depend on each other.
Prep and Done Units
The prep.service and the done.service are plain units and do not define any dependencies.
the prep unit should be configured with Type=oneshot to have it complete before the first task can be started.
By default, Before= and After= only control the order in which services are started. To make sure that the prep unit completes before the tasks are started, use Type=oneshot in the [Service] definition.
Behavior of oneshot is similar to simple; however, it is expected that the process has to exit before systemd starts follow-up units.
The same goes for the task units if the done unit should only be executed after the tasks are complete.
- Custom unit files go into:
- /etc/systemd/system for system-wide scope
- ~/.config/systemd/user for user-specific scope