Writing systemd User Timers


Why am I writing this? I omitted installing the cron package on my Linux box since systemd provides a timers function. Who needs two packages to do one thing? Heh… for a moment I thought maybe I do because some of the documentation out there is a little confusing. Or I’m just dumb. Or both. 😛 So here’s a quick and dirty mash up of the Arch Linux wiki for User units and systemd Timers (because I want this to run when my user is logged in!).

Timers require two files – the service file and the timer file. I’m pretty sure you can call them whatever you want, including different names since the timer config references the service by its name. For simplicity, I named mine the same thing. User unit files live in the .config directory for systemd within your home dir. Here’s mine:

ls -l /home/jimmie/.config/systemd/user

drwxr-xr-x 2 jimmie jimmie 4.0K Jan 16 16:51 default.target.wants
drwxr-xr-x 2 jimmie jimmie 4.0K Jan  4 09:03 sockets.target.wants
drwxr-xr-x 2 jimmie jimmie 4.0K Jan 17 08:53 timers.target.wants
-rw-r--r-- 1 jimmie jimmie  179 Jan 16 16:43 update-blocklists.service
-rw-r--r-- 1 jimmie jimmie  142 Jan 16 16:45 update-blocklists.timer

The timer unit file (update-blocklists.timer) just tells systemd when to do a thing. This unit executes every 180 minutes.

[Unit]
Description=Run "Update Blocklists" every 180 minutes

[Timer]
OnBootSec=3min
OnUnitActiveSec=180min

[Install]
WantedBy=timers.target

The service unit file (update-blocklists.service) tells systemd what to do.

[Unit]
Description=Update Blocklists
After=network.target

[Service]
Type=oneshot
WorkingDirectory=%h
ExecStart=%h/Scripts/update-blocklists.sh

[Install]
WantedBy=default.target

The main parts here give some basic dependencies – execute only after the network is up, do the work in my homedir, and the location of the script to execute. I’m not going to post the contents of the script because you can go find it yourself here.

Once you have the unit files in place (and the script you want to execute, too) you can enable this thing. Trigger a daemon-reload first, then make sure you enter BOTH the service and the timer units on the line or your timer will not work. I know because I tried with one and then the other before using both. When I came back to the machine this morning nothing had updated since I manually triggered the service.

systemctl --user daemon-reload

systemctl --user enable --now update-blocklists.service update-blocklists.timer

Now you can list your user timers:

systemctl --user list-timers --all
NEXT                            LEFT LAST                         PASSED UNIT                    ACTIVATES                
Wed 2024-01-17 11:53:51 EST 2h 59min Wed 2024-01-17 08:53:51 EST 25s ago update-blocklists.timer update-blocklists.service

1 timers listed.

Tada. Easy, right? Lastly, if you need to peep at the logs for your timer, journalctl can filter those! Check it out:

journalctl --user-unit update-blocklists

Happy Hump Day!