Scheduling a task to run automatically — every night at 3 AM, every Monday, every 15 minutes — is one of the oldest tricks on Unix. There are two ways to do it today: the classic cron, and the newer systemd timers. Both are alive and well. This guide shows the same job written both ways so you can pick.
Cron, the old reliable
Every user has their own list of scheduled jobs, called a crontab (cron table). Edit yours with:
crontab -e
(Use sudo crontab -e for root's crontab.) The first time it asks which editor — nano is the friendliest pick.
Each line is one job:
30 3 * * * /usr/local/bin/backup.sh
That runs backup.sh at 3:30 AM every day. The five fields, left to right:
┌──── minute (0-59)
│ ┌── hour (0-23)
│ │ ┌── day of month (1-31)
│ │ │ ┌── month (1-12)
│ │ │ │ ┌── day of week (0-7, 0 and 7 both mean Sunday)
│ │ │ │ │
30 3 * * * /usr/local/bin/backup.sh
* means "every." So 30 3 * * * = minute 30 of hour 3, every day, every month, every weekday.
Common patterns
0 * * * * # every hour, on the hour
*/15 * * * * # every 15 minutes
0 9 * * 1-5 # 9 AM, Monday-Friday
0 0 1 * * # midnight on the 1st of each month
@daily # shortcut for 0 0 * * *
@hourly # shortcut for 0 * * * *
@reboot # at every boot
If you're not sure your expression is right, crontab.guru translates it into English.
List, edit, delete
crontab -l # list your jobs
crontab -e # edit
crontab -r # delete ALL your jobs (no confirmation — careful)
Cron gotchas
- No PATH. Cron runs with a minimal environment.
backupalone won't be found; use the full path/usr/local/bin/backup. - Output goes to email. Anything your script prints is mailed to the user. On a server with no mail set up, this can fill /var/spool/mail. Redirect explicitly:
30 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 - The % sign is special — it's interpreted as a newline. Escape with
\%if your command needs a literal percent. - The script needs to be executable —
chmod +x backup.sh.
systemd timers, the modern alternative
A systemd timer is a small unit file that runs another unit file (the service) on a schedule. More verbose than cron, but with two big wins:
- Logs go through journalctl, like every other service.
- It catches up after downtime — if your laptop was asleep at 3 AM, the job runs when you wake up.
Same backup job, written as a timer
Two files in /etc/systemd/system/:
backup.service — what to run:
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
backup.timer — when to run it:
[Unit]
Description=Run backup nightly
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
[Install]
WantedBy=timers.target
Then enable the timer (not the service):
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
OnCalendar — the schedule mini-language
OnCalendar=hourly
OnCalendar=daily
OnCalendar=weekly
OnCalendar=*-*-* 03:30:00 # 3:30 AM every day
OnCalendar=Mon..Fri 09:00 # 9 AM weekdays
OnCalendar=*-*-01 00:00:00 # midnight on the 1st of each month
Persistent=true is the "catch up" knob — if the machine was off when the timer should have fired, run it as soon as the machine is back.
Useful inspection commands
systemctl list-timers # every timer, when it fires next
systemctl status backup.timer # is this one ticking?
journalctl -u backup.service # what did the last runs say?
Which should you use?
| Cron | systemd timer | |
|---|---|---|
| Setup | One line | Two files |
| Logs | Email or redirect | journalctl out of the box |
| Catch up after downtime | No | Yes (Persistent=true) |
| Available on every distro? | Yes | Only systemd ones (most) |
| Available on FreeBSD? | Yes | No |
For a quick personal job: cron is fewer keystrokes. For a serious service-like task on a Linux server: timers are nicer to live with.