What you'll learn
- Schedule a script with cron
- Read and write the five time fields of a crontab entry
- Avoid the cron environment trap with absolute paths and output redirection
- Prove a job ran and worked, and know when a systemd timer is the better choice
Schedule your backup script with cron, log its output, and know when a systemd timer is the better choice.
Why it matters
A backup script that someone has to remember to run will eventually not be run. Servers rely on scheduled jobs for backups, log clean-up, certificate renewal, report generation and health checks. cronThe classic Linux scheduler, which checks every minute for jobs whose time matches and runs them. is the classic Linux scheduler and is available on nearly every system; systemd timers are the modern alternative. Knowing both, and knowing how to tell whether a scheduled job actually ran, is a core admin skill.
How cron works
The cron service wakes up every minute, reads the schedules (crontabs) and runs any job whose time matches. Each user has their own crontabA user's list of scheduled jobs, each with five time fields and a command, edited with crontab -e., and jobs run as that user.
crontab -l # list your jobs
crontab -e # edit your jobs (opens an editor; choose nano if asked)
crontab -r # remove ALL your jobs: be careful, there is no undoSystem-wide jobs live in /etc/crontab, /etc/cron.d/, and the folders /etc/cron.daily/, /etc/cron.weekly/ and so on, where packages drop their own maintenance scripts.
The five time fields
┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * * command to run* means "every". , lists values, - makes a range and / sets a step.
| Schedule | Meaning |
|---|---|
30 2 * * * | every day at 02:30 |
*/15 * * * * | every 15 minutes |
0 9 * * 1-5 | 09:00 Monday to Friday |
0 0 1 * * | midnight on the first of each month |
@reboot | once, when the system starts |
@daily | once a day (same as 0 0 * * *) |
Times use the server's time zone (check with timedatectl), and many servers run in UTC. A site such as crontab.guru explains any expression in plain English if you want to double-check one.
The cron environment trap
cron does not run your jobs in the same environment as your terminal. Its PATH is minimal (often /usr/bin:/bin), your shell aliases and profile are not loaded, and the working directory is your home folder. That is why "it works when I run it, but not from cron" is so common. The fixes:
- Use absolute paths for the script and for anything it calls.
- Set variables at the top of the crontab if needed:
PATH=/usr/local/bin:/usr/bin:/bin. - Make the script itself robust (shebang,
set -euo pipefail, checks), as in the previous lesson.
Capture the output
By default cron tries to email a job's output to the user, which usually goes nowhere on a server. Redirect it to a log file instead:
30 2 * * * /home/alex/bin/backup.sh /home/alex/projects /home/alex/backups >> /home/alex/backup.log 2>&1>> appends standard output to the log, and 2>&1 sends errors to the same place. Because the script exits non-zero on failure and prints "ERROR: ...", the log tells you exactly what went wrong.
Did it run?
grep CRON /var/log/syslog | tail # Ubuntu: cron records each job it starts
journalctl -u cron --since today # the same via the journal (unit "crond" on Red Hat)
tail /home/alex/backup.log # your job's own output
ls -lt /home/alex/backups | head -3 # and the result itselfThe syslog entry proves cron started the job. Only your log and the result prove it worked. For important jobs, monitor the result: alert if the newest backup is older than a day.
Worked example: test with a short interval first
Do not wait until 02:30 to find out a job is broken. Schedule it every two minutes, watch it run, then change it to the real time.
*/2 * * * * /home/alex/bin/backup.sh /home/alex/projects /home/alex/backups >> /home/alex/backup.log 2>&1After a few minutes, check the log and the backups folder. When it works, crontab -e again and change */2 * * * * to 30 2 * * *.
Avoid overlapping runs
If a job can take longer than its interval, two copies can run at once. flock prevents that:
*/5 * * * * /usr/bin/flock -n /tmp/backup.lock /home/alex/bin/backup.sh /home/alex/projects >> /home/alex/backup.log 2>&1systemd timers, in brief
A timerA systemd unit that triggers a service on a schedule, with journald logging and optional catch-up of missed runs. unit triggers a service unit on a schedule. Advantages: output goes to the journal automatically (journalctl -u backup), Persistent=true runs a job that was missed while the machine was off, and systemctl list-timers shows when each timer last ran and will next run.
# /etc/systemd/system/backup.timer
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.targetIt needs a matching backup.service with ExecStart= pointing at the script, then sudo systemctl daemon-reload and sudo systemctl enable --now backup.timer. Use cron for simple per-user jobs; reach for timers when you want journald logging, missed-run catch-up or dependencies on other services.
Common mistakes
- Relative paths or commands not on cron's PATH.
- No output redirection, so failures vanish silently.
- Confusing the fields, for example
* 2 * * *, which runs every minute from 02:00 to 02:59. - Forgetting the time zone of the server.
- Running
crontab -rwhen you meantcrontab -e. Keep a copy:crontab -l > ~/crontab.backup. - Only checking that cron started the job, never that it succeeded.
Recap
- cron runs jobs from each user's crontab on a five-field schedule (
minute hour day month weekday). - Use absolute paths, redirect output with
>> log 2>&1, and test with a short interval first. - Check
grep CRON /var/log/syslogorjournalctl -u cron, your log file and the result. - systemd timers add journald logging, catch-up of missed runs and
systemctl list-timers.
Try it
The Do tab has you schedule your backup script, prove it ran, and move it to a real nightly time.
Key terms (5)
- cron
- The classic Linux scheduler, which checks every minute for jobs whose time matches and runs them.
- crontab
- A user's list of scheduled jobs, each with five time fields and a command, edited with crontab -e.
- 2>&1
- A shell redirection that sends standard error to the same place as standard output, such as a log file.
- flock
- A command that takes a lock file so a new run of a job does not start while the previous one is still running.
- timer
- A systemd unit that triggers a service on a schedule, with journald logging and optional catch-up of missed runs.