systemd-run
can schedule a command to be run at a specific time, as long as:
- the system is not powered down before that time, because it writes a transient timer & service file to /run, which is cleared at reboot, and
- the system is not suspended during that specific time, unless you use the "WakeSystem" option (see below).
See even further below for a reboot-persistent option.
A simple example for systemd-run
:
# generate an arbitrary timestamp, in seconds-since-the-epoch
timestamp=$(date -d 'now + 42 seconds' +%s)
systemd-run --on-calendar "$(date -d @"$timestamp" +'%F %T')" \
--timer-property=AccuracySec=1us \
touch /tmp/done
The important parts to get systemd-run
to use a seconds-since-the-epoch timestamp are:
- to use
--on-calendar
and
- here, using GNU date to convert the seconds-since-the epoch timestamp into a format that
systemd-run
understands. You could input or convert the timestamp yourself, as long as the result is in a format that OnCalendar understands.
The current documentation for OnCalendar indicates that it directly supports input in a seconds-since-the-epoch timestamp. That '@seconds' support was added in systemd version 234 (search that page for the file src/basic/calendarspec.c
) in commit d80e5b7. Your version of systemd may predate this change; for examples:
- RHEL 7 started with systemd 208 and was later upgraded to version 219; RHEL 8 uses version 239 (ref).
- Debian 8 used version 215, Debian 9 used version 232, and Debian 10 uses version 241 (ref).
- Ubuntu 16.04 LTS had version 229, Ubuntu 17.10 had version 234, and Ubuntu 18.04 LTS had version 237 (ref).
With a new-enough version of systemd, you could use this invocation:
timestamp=$(date -d 'now + 42 seconds' +%s)
systemd-run --on-calendar "@${timestamp}" \
--timer-property=AccuracySec=1us \
touch /tmp/done
I've also shown a timer-property of AccuracySec, which defaults to 1 minute, and whose value you should adjust based on the documentation:
To optimize power consumption, make sure to set this value as high as possible and as low as necessary.
I haven't tested it, but there is also a timer option called WakeSystem
which would:
cause the system to resume from suspend, should it be suspended and if the system supports this. Note that this option will only make sure the system resumes on the appropriate times, it will not take care of suspending it again after any work that is to be done is finished.
You would integrate it into the above like so:
timestamp=$(date -d 'now + 42 seconds' +%s)
systemd-run --on-calendar "$(date -d @"$timestamp" +'%F %T')" \
--timer-property=AccuracySec=1us \
--timer-property=WakeSystem=true \
touch /tmp/done
To ask systemd to run a command at a certain time, in a way which would persist beyond a reboot, and which is independent of any particular login session, you would need to place timer and service unit files under /etc/systemd/system/
. /etc/systemd/system is where local configuration is stored for systemd.
A sample timer file would be:
[Timer]
AccuracySec=1us
[Unit]
Description=2020-01-22 09:50:00 timer
[Timer]
OnCalendar=2020-01-22 09:50:00
and a sample service file would be:
[Unit]
Description=my 2020-01-22 09:50:00 service
[Service]
ExecStart=
ExecStart=@/usr/bin/bash "/usr/bin/bash" "-c" "echo 2020-01-22 09:50:00 timer and service ran > /tmp/done"
You would then inform systemd about the files by reloading it:
systemctl daemon-reload
... and then enabling the timer:
systemctl enable foo.timer
... and then starting the timer:
systemctl start foo.timer
You may want to periodically remove expired timer and service units from /etc/systemd/system, once their time has passed.
at
command. – UncaAlby Dec 31 '19 at 17:15at
. This question is about doing it with systemd. – a3nm Jan 01 '20 at 18:17atd
is an old tool whose functionality is supposed to be captured by systemd, so with more and more programs depending on systemd it makes sense for me to use systemd for this task too. – a3nm Jan 03 '20 at 17:40