Let me pick up the POP3 example you recently mentioned in a comment. It is a good example, because POP3 can not notify the arrival of new mail. (For IMAP access I would not use a timer solution, because IMAP actually can notify!)
Cron
crontab -e
-e
stands for edit. Caution: launching without options does not give help, but flushes all your existing cron jobs if you do not manage to get out of the editor without saving.—And that definitively means to lose work if you had existing jobs.
So, in the edit mode, add a line:
30 14,21 * * * mpop --quiet
which launches mpop
2:30pm and 9:30pm.
In theory you could also have edited /var/spool/cron/crontabs/username
, but most crons would not notice and the directory permissions often do not allow direct access to the file.
Cron jobs are less joy to debug. More often than not you end up scheduling it every minute until you trust it and then set the frequency correctly.
Otherwise it is a pretty quick interface for job repetition.
These days, don’t be surprised when you have no cron daemon running. Package systemd-cron
(at least on Debian) serves you systemd timers with a cron user interface: it translates the crontab line transparently for you and the command line tools are the same.—The other way around will probably never exist.
Systemd timer
loginctl enable-linger
has to be run once (and never again). It makes sure that the user mode systemd is launched on boot. Otherwise the first login would launch it.
systemctl --user edit --full --force mpop.service
You can also run just systemctl edit mpop.service
to get successive errors and hints guiding you to the correct command line with options:
No files found for mpop.service.
Run 'systemctl edit --force --full mpop.service' to create a new unit.
Running the suggested command would reveal permission problems with writing the file and ask for a password to reload the system daemon.—At this point you should remember to add --user
to talk to the user systemd instance. Note: you could also go for the system instance, but I’m not describing that here. It is similar.
Unlike with cron, you could really go directly for the file ~/.config/systemd/user/mpop.service
, but you have to remember the path yourself. And in some cases systemd --user daemon-reload
is needed. E.g. if the file is actually existing and loaded. systemd --user edit ...
does a daemon-reload in any case so you don’t have to.
So, in the mpop.service
file you add:
[Service]
ExecStart=mpop --quiet
You can test run this with systemctl --user start mpop
. mpop
is short for mpop.service
here.
Check details with systemctl --user status mpop
or the full output of past runs with journalctl --user -u mpop
(-u
is short for --unit
).
With systemd, the timer is another unit which triggers the service unit.
Create one with:
systemctl --user edit --full --force mpop.timer
And save the following in the editor:
[Timer]
OnCalendar=*-*-* 14,21:30:00
[Install]
WantedBy=timers.target
The OnCalendar=
can be written shorter. As the Unit=
to activate is not written explicitly, the service by the same name will be used.
Like the service (short lived), the timer (long living) will initially be off. The service is activated by the timer, so no worries here.
But what activates the timer?—This is where the [Install]
section comes into play. It is related to the systemctl --user enable/disable ...
and they all have very unfortunate names.
The section should be called [Hooks]
and the commands ... hook/unhook ...
. Reasons:
- the timer is already installed with the existence of the
mpop.timer
file
- would reveal the relationship between the section and the commands
- the
enable
command might not enable the unit if it is hooked to a siding unit or a non-existing one (you get a warning for the latter, though), but it will show as “enabled”.
- the
mpop.service
unit has no [Install]
section and enable/disable
do nothing on it, it is shown as “static”, even though it is as “enabled” as it gets
Having that said, you can see that the WantedBy=
hook mentions a well-known target, one which is started in normal circumstances and the one supposed to be used for timers.
Use systemctl --user list-dependencies default.target
now and after the following steps to see what happens around timers.target
:
systemctl --user enable mpop.timer # hooks it into the timers.target
systemctl --user start timers.target # this or reboot
I consider executing start
on timers.target
instead of mpop.timer
(both work) better practice. It helps detecting spelling mistakes in the [Install]
section of the timer unit and follows closer the execution path on reboots.
cron
has priority important, though it doesn't try very hard to stop me from removing it. The only thing my system has that actually depends on it ismunin
, which is a little surprising.