☁ Cloud & DevOps

Cron and systemd timers: automating scheduled jobs

How to schedule backups, cleanups and reports on Linux with cron and systemd timers: syntax, differences, logs, overlapping jobs and failure notifications.

Every server has tasks to carry out at regular intervals: the nightly backup, cleaning up temporary files, renewing a certificate, sending a weekly report. On Linux there are two tools for this: the classic cron, around for decades, and systemd timers, more recent and richer. Both work well, provided you know their pitfalls.

Cron: the syntax

Cron reads crontabs, files in which each line describes a job with five time fields followed by the command:

# minute  hour  day-of-month  month  day-of-week  command
30 2 * * *   /usr/local/bin/backup.sh
0 */6 * * *  /usr/local/bin/sync.sh
15 8 * * 1   /usr/local/bin/weekly-report.sh

The first line runs the backup every day at 2:30; the second every six hours on the hour; the third every Monday at 8:15. The crontab -e command edits the current user's crontab, while files in /etc/cron.d/ let you define system jobs, also specifying the user to run them as.

The pitfalls of cron

The environment is almost empty

The most common problem is a job that works when launched by hand and fails from cron. The reason is almost always the environment: cron runs commands with a minimal PATH and without the variables your shell loads at login. The solution is to use absolute paths for programs and files, or to define the necessary variables at the top of the script.

Output gets lost

By default cron tries to email the output of commands. On a server without mail configured, that output vanishes, error messages included. It is good practice to redirect output to a log file or, better, to the system log.

The percent character

In a crontab the % symbol has a special meaning and must be preceded by a backslash. A command such as date +%Y-%m-%d written directly in the crontab doesn't do what you expect. Moving the logic into a script eliminates the problem.

Jobs lost while the server is off

If the server is off at the scheduled time, cron does not catch up on the missed job. On a laptop or a machine that reboots often, a nightly backup can be skipped for days without anyone noticing.

systemd timers

On distributions that use systemd you can schedule a job with two files: a service, which describes what to run, and a timer, which describes when.

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Runs the backup every night

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10min

[Install]
WantedBy=timers.target

You enable it with systemctl enable --now backup.timer and check it with systemctl list-timers, which shows the last and next run of every timer.

What they offer in addition

  • Persistent=true catches up on missed jobs: if the server was off at the scheduled time, the job starts as soon as it comes back on.
  • Built-in logging: all output goes to the journal, readable with journalctl -u backup.service.
  • No overlap: if the previous run is still in progress, the timer does not start a second instance of the same service.
  • Random delay: RandomizedDelaySec spreads out start times, useful when many servers would hit the same service at the same instant.
  • Resource limits: you can cap the job's CPU and memory with the usual systemd options.
  • Dependencies: a job can start only after the network or a disk is available.

Quick comparison

Aspect cron systemd timer
Ease of writing One line Two files
Jobs missed while the machine is off Not recovered Recoverable
Logs Must be configured Built into the journal
Overlapping runs Possible Avoided for the same service
Portability Almost everywhere Only systems with systemd

Good practices for either tool

Robust scripts

Start shell scripts with set -euo pipefail, so they stop at the first error instead of carrying on with incomplete data. A backup script that continues after an error can overwrite a valid copy with an empty one.

Avoiding overlaps with cron

If you use cron for a job that can last longer than its interval, protect it with flock, which prevents a second copy from starting until the first has finished:

*/10 * * * * flock -n /tmp/sync.lock /usr/local/bin/sync.sh

Knowing when something doesn't start

Silence is the enemy of scheduled jobs: a backup that fails without warning is worse than no backup, because it gives a false sense of security. An effective technique is the reverse check: on successful completion, the script contacts a monitoring service. If the signal doesn't arrive by the expected time, the service sends an alert. This way you also discover jobs that never started at all, not just those that failed.

Watch out for time zones

Times refer to the server's time zone. With daylight saving time, a job scheduled at a time that is skipped or repeated during the change may not run or may run twice. Many administrators set servers to UTC to avoid surprises.

In short

  • Cron is simple and universal; systemd timers offer catch-up of missed jobs, logging and protection from overlaps.
  • In cron use absolute paths, redirect output and move the logic into scripts.
  • With systemd, Persistent=true and journalctl solve two of the most common problems.
  • Write scripts that stop at the first error and avoid overlapping runs.
  • Use a reverse check to know when a job fails or doesn't start at all.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. With degrees in computer science and psychology, he combines technical analysis with an understanding of the human factor.

Read also