crontab.in
monitor with cron.watch
linux scheduling

Scheduling on Linux: cron & systemd.

Three ways Linux runs jobs on a schedule — classic cron, systemd timers, and anacron — when to use each, and how their syntaxes map to one another.

Classic cron (user & system)

The simplest option, available everywhere:

crontab -e          # per-user jobs

# /etc/cron.d/myapp  — system job (note the user column)
*/5 * * * *  appuser  /opt/app/sync.sh

There are also drop-in directories run by cron: /etc/cron.hourly, cron.daily, cron.weekly, and cron.monthly — put an executable script there and forget the schedule.

systemd timers

On modern distros, systemd timers are the more powerful alternative: better logging (journalctl), dependency handling, randomized delays, and catch-up after downtime. A timer needs two units — a .service and a .timer:

# /etc/systemd/system/backup.service
[Service]
Type=oneshot
ExecStart=/opt/backup.sh

# /etc/systemd/system/backup.timer
[Timer]
OnCalendar=*-*-* 03:00:00      # daily at 3am  (= cron 0 3 * * *)
Persistent=true                # run on next boot if missed

[Install]
WantedBy=timers.target
sudo systemctl enable --now backup.timer
systemctl list-timers          # see all timers + next run

OnCalendar uses its own syntax: daily, hourly, Mon-Fri 09:00, *-*-01 00:00:00 (first of month). Test any expression with systemd-analyze calendar "Mon-Fri 09:00".

anacron (for machines that sleep)

cron assumes the machine is always on — a job scheduled while the laptop is asleep is simply skipped. anacron fixes that: it tracks when each job last ran and catches up after boot. systemd timers with Persistent=true give you the same behavior.

Timezones

A cron expression has no timezone — a crontab runs in the machine’s local time. To pin a job to a specific zone, set CRON_TZ at the top of the crontab (an IANA name like America/New_York):

CRON_TZ=America/New_York
0 9 * * 1-5  /opt/report.sh   # 09:00 in New York, DST-aware

Vixie cron / cronie honor CRON_TZ; some builds also read a plain TZ= line, but CRON_TZ is the portable one for scheduling.

systemd timers run in system local time, but since systemd v252 you can append a zone to OnCalendar:

[Timer]
OnCalendar=Mon..Fri 09:00 America/New_York   # systemd >= 252
Persistent=true

💡 DST gotcha: a wall-clock time that is skipped or repeated on a changeover day can run zero or two times. For critical jobs avoid 01:00–03:00 local, or schedule in UTC.

Which should I use?

Monitor it free with cron.watch

Whether you pick cron or a systemd timer, neither will page you when the job stops running. A schedule is only a promisecron.watch tells you if the job actually ran, alerts you when it silently fails, skips, or runs long, and keeps a history of every run.

# cron / cron.d
0 3 * * *  root  /opt/backup.sh && curl -fsS https://cron.watch/s/your-id

# systemd: add a check-in to the service
[Service]
Type=oneshot
ExecStart=/opt/backup.sh
ExecStartPost=/usr/bin/curl -fsS https://cron.watch/s/your-id
Get cron.watch free ↗ Analyze your crontab →

More scheduling guides