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?
- Quick job, any system: a user crontab. Nothing to learn.
- Production service: a systemd timer — real logs, status, and missed-run catch-up.
- Laptop / intermittent host: anacron or a
Persistent=truetimer.
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 promise — cron.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