If you have a Bash script that needs to run periodically, you can run it using a crontab entry or file. But you can also have it invoked from Systemd using systemd.timer.
Running via Systemd provides more powerful constructs for invocation, configuration, monitoring, and logging. In this article, I will show how to periodically run a simple Bash script using a system-level Systemd timer.
If you want to run a user-level Systemd timer, see my other article.
Prerequisites
Install the package prerequisites and download the github project code.
# packages sudo apt install curl git -y # pull project git clone https://github.com/fabianlee/systemd-periodic-bash.git cd systemd-periodic-bash
Running Systemd service
Now use the supplied script to create the Systemd service and timer, and run the Bash script every 60 seconds. We will go into the details of how this works in the upcoming sections.
cd systemd-systemlevel # create user, systemd files, start ./create-simple-service.sh # configure environment variable # set 'env_arg' to any value, I set mine to 'my foo' sudo vi /etc/default/simple
If you tail the logs, you should see output similar to below every 60 seconds.
$ tail -f /var/log/simple/simple.log Jan 3 18:15:21 mybox1 simpleservice[19070]: ======================== Jan 3 18:15:21 mybox1 simpleservice[19071]: Mon 03 Jan 2022 06:15:20 PM EST Jan 3 18:15:21 mybox1 simpleservice[19070]: ======================== Jan 3 18:15:21 mybox1 simpleservice[19070]: env_arg environment variable: my foo Jan 3 18:15:21 mybox1 simpleservice[19070]: loop counter 1 Jan 3 18:15:21 mybox1 simpleservice[19070]: loop counter 2 Jan 3 18:15:21 mybox1 simpleservice[19070]: loop counter 3
Overview
Because we are running this Bash script via Systemd, we get a lot of production-level features over using a simple cron. Basically, we get all the power of Systemd.
- Invocation – run once at boot, periodically, or after the network is initialized
- Isolation – as a specific user or group
- Configuration – pull in properties from ‘/etc/default’ or other locations
- Monitoring – process monitored for success/error, watchdog process
- Logging – to syslog, file
Systemd service unit file
Our simple.service file is shown below. The “$scriptdir” value is a placeholders, replaced by create-simple-service.sh.
[Unit] Description=simple systemd bash script ConditionPathExists=$scriptdir/simple-bash.sh After=network.target [Service] #Type=simple Type=forking User=simpleservice Group=simpleservice # define 'env_var' env var, secured by root ownership and mode 600 EnvironmentFile=-/etc/default/simple WorkingDirectory=$scriptdir ExecStart=$scriptdir/simple-bash.sh 3 # make sure log directory exists and owned by syslog PermissionsStartOnly=true ExecStartPre=/bin/mkdir -p /var/log/simple ExecStartPre=/bin/chown syslog:adm /var/log/simple ExecStartPre=/bin/chmod 755 /var/log/simple StandardOutput=syslog StandardError=syslog SyslogIdentifier=simpleservice
This has the effect of:
- Starting the service after the network is initialized (After=network.target)
- Running the service using the ‘simpleservice’ user (User=simpleservice)
- Loading environment keys from an optional default file (EnvironmentFile)
- Invoking the Bash script with a command line argument (ExecStart)
- Hooks that run before the invocation (ExecStartPre)
- Sending output of script to syslog (StandardOutput,SyslogIdentifier)
service and timer files
The .service and .timer files used by Systemd are kept in “/lib/systemd/system” for system-level services.
These are enabled and started like this.
service=simple # enable and start service and timer sudo systemctl enable $service.service $service.timer sudo systemctl start $service.service sudo systemctl start $service.timer
REFERENCES
NOTES
check that user level systemd process is running
# you should see 'lib/systemd/systemd --user' ps aux | grep systemd | grep "^$USER"
tried reinstalling dbus
sudo apt install --reinstall dbus sudo apt install --reinstall dbus-user-session
checked env vars
id -u id -un echo $XDG_RUNTIME_DIR echo $DBUS_SESSION_BUS_ADDRESS
tried enabling linger
loginctl enable-linger $USER
this showed errors, so restarted
# tried restart of login service sudo systemctl restart systemd-logind. service # then errors were shown in user system, so restarted sudo systemctl status user@1000.service --no-pager -l sudo systemctl restart user@1000.service # then this worked systemctl --user status