Scheduling: cron & systemd Timers
Servers do a lot of work while nobody is watching: nightly backups, clearing old files, renewing certificates, sending reports. Linux has two schedulers for this. cron is the classic one, around since the 1970s, with a compact one-line syntax. systemd timers are the modern alternative, with better logging and more control.
In this lesson you will write cron jobs, understand the five time fields, avoid the classic "it works in my terminal but not in cron" bug, and then do the same jobs with a systemd timer.
cron and crontab
cron is a background service that wakes up every minute and runs any job whose time has come. Each user has their own job list, called a crontab (cron table). You never edit the file directly; use the crontab command:
crontab -e
crontab -l
crontab -r-e opens your crontab in your editor (the first time, Ubuntu asks which editor you want), -l lists it, and -r deletes the whole thing without asking, so be careful not to type -r when you meant -e. If crontab is not found, install it with sudo apt install cron.
The five time fields
Each line in a crontab is one job: five time fields, then the command.
# ┌──────── minute (0-59)
# │ ┌────── hour (0-23)
# │ │ ┌──── day of month (1-31)
# │ │ │ ┌── month (1-12)
# │ │ │ │ ┌ day of week (0-7, 0 and 7 are Sunday)
# │ │ │ │ │
30 2 * * * /home/priya/bin/backup.shThis runs backup.sh at 02:30 every day. In each field, * means "every", and you can use lists, ranges and steps:
| Schedule | Meaning |
|---|---|
0 * * * * | At minute 0 of every hour |
*/15 * * * * | Every 15 minutes |
30 18 * * 1-5 | 18:30 on weekdays (Monday to Friday) |
0 9 1 * * | 09:00 on the 1st of every month |
0 0 * * 0 | Midnight every Sunday |
0 8,20 * * * | 08:00 and 20:00 every day |
@reboot | Once, when the system starts |
@daily, @weekly, @hourly | Shortcuts for common schedules |
Times use the server's time zone (timedatectl shows it). A cloud server is often set to UTC, so 02:30 in cron would be 08:00 in India.
A real crontab
Here is Priya's crontab after editing it with crontab -e:
crontab -l# m h dom mon dow command MAILTO="" 30 2 * * * /home/priya/bin/backup.sh >> /home/priya/logs/backup.log 2>&1 */10 * * * * /usr/bin/curl -fsS -o /dev/null https://shop.example.in/health || echo "site down $(date)" >> /home/priya/logs/health.log 0 3 * * 0 /usr/bin/find /home/priya/tmp -type f -mtime +7 -delete
The first job runs the backup script every night and appends both its output and errors to a log file. The second checks every ten minutes that the website answers and records the time if it does not. The third deletes temporary files older than a week, every Sunday at 03:00. MAILTO="" stops cron from trying to email output.
System-wide cron jobs
Administrators also use system locations. /etc/crontab and files in /etc/cron.d/ have an extra user field after the five time fields. Scripts dropped into /etc/cron.daily/, /etc/cron.weekly/ or /etc/cron.monthly/ run at those intervals with no time fields at all; packages use these folders.
# /etc/cron.d/sweets-report
15 7 * * 1 sweets /opt/sweets-api/.venv/bin/python /opt/sweets-api/weekly_report.pyThis runs a weekly report as user sweets every Monday at 07:15. Files in /etc/cron.d/ must be owned by root and should not be writable by others.
systemd timers
A systemd timer is a .timer unit that starts a matching .service unit on a schedule. It takes two small files instead of one line, but it brings real advantages: the job's output goes to the journal automatically, you can test the job on its own with systemctl start, missed runs can catch up after the machine was off, and systemctl list-timers shows when everything will run next.
In the previous lesson you created backup-notes.service. Add /etc/systemd/system/backup-notes.timer:
[Unit]
Description=Run the notes backup every night
[Timer]
OnCalendar=*-*-* 02:30
Persistent=true
RandomizedDelaySec=10min
[Install]
WantedBy=timers.targetOnCalendar uses systemd's calendar format, weekday year-month-day hour:minute, so *-*-* 02:30 means every day at 02:30. Persistent=true runs a missed job at the next boot if the machine was off at 02:30. RandomizedDelaySec spreads the start over ten minutes, which avoids many servers hitting a backup system at the same second. Enable the timer, not the service:
sudo systemctl daemon-reload
sudo systemctl enable --now backup-notes.timer
systemctl list-timers backup-notes.timerNEXT LEFT LAST PASSED UNIT ACTIVATES Sat 2026-10-31 02:34:12 IST 14h - - backup-notes.timer backup-notes.service 1 timers listed. Pass --all to see loaded but inactive timers, too.
The next run is shown with the random delay already applied. Results appear in journalctl -u backup-notes.
Checking calendar expressions
systemd-analyze calendar explains any expression and shows the next run time, which removes the guesswork:
systemd-analyze calendar "Mon..Fri 18:30"
systemd-analyze calendar "*-*-01 09:00" Original form: Mon..Fri 18:30
Normalized form: Mon..Fri *-*-* 18:30:00
Next elapse: Fri 2026-10-30 18:30:00 IST
(in UTC): Fri 2026-10-30 13:00:00 UTC
From now: 7h left
Original form: *-*-01 09:00
Normalized form: *-*-01 09:00:00
Next elapse: Sun 2026-11-01 09:00:00 IST
(in UTC): Sun 2026-11-01 03:30:00 UTC
From now: 1 day 21h leftSimple words work too: OnCalendar=daily, weekly, hourly. For "every 15 minutes", write *:0/15.
Advanced: one-off jobs
For a job that should run only once, later today, use a transient timer: sudo systemd-run --on-active=30min /usr/bin/systemctl restart sweets-api restarts the API in half an hour. The classic tool for this is at (sudo apt install at), used as echo "/home/priya/bin/backup.sh" | at 23:00.
cron or timer?
Use cron for quick personal jobs and when you need the same line to work on any Unix system. Use systemd timers for server jobs you care about: you get logging, dependency control, resource limits and catch-up after downtime for free.
Common mistakes
- Relying on
PATHand aliases in cron. Use full paths and test with the same minimal environment. - No log. Redirect output with
>> file 2>&1, or use a timer whose output goes to the journal. - Unescaped
%in crontab. Write\%. - Enabling the
.serviceinstead of the.timer. Enable and start the timer; it starts the service. - Forgetting the time zone. Check
timedatectlbefore choosing hours on a cloud server.
Key points
- cron runs jobs from each user's crontab; edit with
crontab -e, list withcrontab -l. - The five fields are minute, hour, day of month, month and day of week;
*,,,-and/build schedules. - Use full paths and redirect output in cron jobs, because cron's environment is minimal.
- A systemd timer (
.timer+.service) usesOnCalendar=; enable it withsystemctl enable --now name.timer. systemctl list-timersshows upcoming runs andsystemd-analyze calendartests expressions.
Exercise
Write cron lines for: (1) running /home/kabir/bin/report.sh at 06:45 every Monday, logging output to /home/kabir/report.log; (2) clearing /home/kabir/cache of files older than 3 days every night at midnight. Then write the [Timer] section of a systemd timer that runs a service every 15 minutes, catching up after downtime.
Show solution
45 6 * * 1 /home/kabir/bin/report.sh >> /home/kabir/report.log 2>&1
0 0 * * * /usr/bin/find /home/kabir/cache -type f -mtime +3 -delete[Timer]
OnCalendar=*:0/15
Persistent=truesystemd-analyze calendar "*:0/15" Original form: *:0/15
Normalized form: *-*-* *:00/15:00
Next elapse: Fri 2026-10-30 11:45:00 IST
(in UTC): Fri 2026-10-30 06:15:00 UTC
From now: 9min left1 in the fifth field means Monday. *:0/15 means "every hour, starting at minute 0, every 15 minutes", and systemd-analyze confirms it before you rely on it.
Quick Quiz
1. What does */15 * * * * mean in a crontab?
*/15 in the minute field means every 15th minute.
2. Why does a script that works in your terminal fail under cron?
Use full paths and do not depend on aliases or variables from .bashrc.
3. Which unit do you enable to schedule a systemd job?
The timer starts the service at the scheduled times.
4. What does Persistent=true do in a timer?
Persistent timers catch up on runs that were missed while the system was down.
Learn Linux with a mentor
Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
