Cronjob vs systemd timers - how to create a systemd timer

Posted by ryansouthgate on 11 Mar 2025

Intro

I’ve been using cronjobs for a long time. More recently I’ve been testing systemd-timers as they allow dependency management, so I don’t have to check (in the script I’m running) to see if certain services are up/running before my scripts can do their work.

In this post I’m going to demonstrate how to create a simple systemd timer service which runs a script on a schedule

A quick comparison with Cron

I’m all for learning to use new tools. I think the best way to compare tools is to just jump in a give it a go. After a quick read online, I’ve distilled some of the benefits/drawbacks:

Some benefits of using systemd over cron:

  • dependency management - systemd allows you to define dependencies between services. Which allows you to order your services, when others are running and functional
  • logging - detailed logs and status reports can be viewed using journalctl - which makes it simple to check the status of a task and view it’s run logs. Whereas cron requires you to manually make arrangements for logging

some drawbacks of using systemd over cron:

  • complexity - it’s more complex to setup a systemd job over a cronjob. cronjobs are incredibly simple to create and run
  • systemd needs to be installed and configured (if it does not come with your OS already). cronjobs are widely supported across all unix-like systems.
  • cronjobs have been around longer, which mean they’re more well-tested, reliable and understood by others.

Setting up the job

First off we’ll need a script that will be run on a schedule. For this example, the following simple script will be run on a schedule:

#!/usr/bin/env bash

# print out the date and time
echo "This is a test: $(date)"

Your scripts will likely be a whole lot more complex. Some of my timed jobs I have, and their scripts, do things like moving files/directories to a NAS (network attached storage) device.

This script is saved in /home/ryan/scripts/print-date.sh. Obviously chmod’d too.

Systemd is largely configuration based. We’ll have to create two files; one for the service definition and another for the timer definition. The convention for these files look like so:

Service: print-date.service Timer: print-date.timer

The service definition, looks like this:

[Unit]
Description=Prints the date/time for demonstration purposes of systemd timers
Wants=print-date.timer

[Service]
User=ryan
Group=ryan
Type=oneshot
ExecStart=/home/ryan/scripts/print-date.sh

[Install]
WantedBy=multi-user.target

The timer definition, looks like this:

[Unit]
Description=Timer for: print-date.service
Requires=print-date.service

[Timer]
Unit=print-date.service
# Run the timer, every hour, on the hour
OnCalendar=*-*-* *:00:00

[Install]
WantedBy=timers.target

Now that we’ve set up the definitions, they’re ready to be added to systemd. Copy both of these files over to the systemd directory, using the command below:

sudo mv print-date.* /etc/systemd/system

Systemd will be unaware that you’ve moved some files and now want them to be added. So we’ll have to run a couple more commands, the first is to reload the systemd daemon, which will search for the systemd service we added:

sudo systemctl daemon-reload

Systemd is now aware of our service, however it doesn’t automatically enable it and start the timer. To do that, we do:

sudo systemctl enable print-date.timer
sudo systemctl start print-date.timer

We only have to run the above once, the above timer will persist across machine shutdowns/restarts.

Now, on the hour, every hour. Our service will print the date & time to the console. To view the status of our timer (the thing that triggers our service), run:

systemctl status print-date.timer

Which gives the output of:

● print-date.timer - Timer for: print-date.service
     Loaded: loaded (/etc/systemd/system/print-date.timer; enabled; preset: enabled)
     Active: active (waiting) since Mon 2025-03-03 09:24:22 UTC; 2h 10min ago
    Trigger: Mon 2025-03-03 12:00:00 UTC; 25min left
   Triggers: ● print-date.service

Mar 03 09:24:22 ryan-laptop systemd[1]: Started print-date.timer - Timer for: print-date.service.

The above output shows the current status of the timer (enabled), it also shows the time until the next run event.

To view the details of the service, and it’s logs, run the command:

systemctl status print-date.service

Which will give the following output:

○ print-date.service - Prints the date/time for demonstration purposes of systemd timers
     Loaded: loaded (/etc/systemd/system/print-date.service; disabled; preset: enabled)
     Active: inactive (dead) since Mon 2025-03-03 11:00:28 UTC; 42min ago
TriggeredBy: ● print-date.timer
    Process: 36385 ExecStart=/home/ryan/scripts/print-date.sh (code=exited, status=0/SUCCESS)
   Main PID: 36385 (code=exited, status=0/SUCCESS)
        CPU: 20ms

Mar 03 11:00:27 ryan-laptop systemd[1]: Starting print-date.service - Prints the date/time for demonstration purposes of systemd timers...
Mar 03 11:00:27 ryan-laptop print-date.sh[36388]: This is a test: Mon  3 Mar 11:00:27 GMT 2025
Mar 03 11:00:28 ryan-laptop systemd[1]: print-date.service: Deactivated successfully.
Mar 03 11:00:28 ryan-laptop systemd[1]: Finished print-date.service - Prints the date/time for demonstration purposes of systemd timers.

The above output shows you a minimal view of the logs from the last run.

To get a view of all of the logs, since the inception of your service. You can run the following command (which also supports filtering by date etc);

journalctl -u print-date.service

My thoughts

The one thing I love about systemd timers is the log capturing ability shown above. Now, I don’t have to take care of logging myself. I can just output to the terminal and systemd will capture that for me, using a tool like journalctl enables me to properly search and understand the logs. Debugging and observability is made so much easier

Closing

I hope this post has been helpful and given a quick instight into how to get a simple systemd timer up and running and why you might want to consider it over a cronjob.

Thanks for reading!



comments powered by Disqus