10
Cron & Timers
- Why cron job silent-failed
- Read crontab schedule
- List systemd timers
Must-know cold
- Cron fields:
min hour dom mon dow command(5 time fields + command) crontab -l·crontab -e· absolute paths + redirect logssystemctl list-timers --all·systemctl status job.timer- Silent failure classics: PATH,
%in the command, bashisms (SHELL=/bin/sh), no logging, wrong user - Prefer
flockto prevent overlap
Cron field reference
Definition: Five time fields (min hour dom mon dow) that schedule when a cron job runs.
* * * * * command
│ │ │ │ │
│ │ │ │ └── day of week (0–7; 0 and 7 = Sunday)
│ │ │ └───── month (1–12)
│ │ └──────── day of month (1–31)
│ └─────────── hour (0–23)
└────────────── minute (0–59)
| Expression | Meaning |
|---|---|
* | every |
*/5 | every 5 units |
1,15,30 | list |
1-5 | range |
0 2 * * * | daily 02:00 |
5 2 * * 0 | Sundays 02:05 |
0 */6 * * * | 00:00, 06:00, 12:00, 18:00 (clock hours, not “every 6h from now”) |
0 9 1 * * | 09:00 on day-of-month 1 |
Special strings (Vixie/cronie): @reboot @hourly @daily (@midnight) @weekly @monthly @yearly (@annually). @reboot runs when cron starts, not necessarily at kernel boot.
Commands
crontab
Definition: Edit, list, or remove the per-user cron schedule table.
| Option | Argument | Meaning | Example |
|---|---|---|---|
-l | — | List current user crontab | crontab -l |
-e | — | Edit crontab in $EDITOR | crontab -e |
-r | — | Remove entire crontab | Dangerous; confirm first |
-i | — | With -r: prompt before remove | crontab -ir |
-u | USER | Act on USER’s crontab (root) | sudo crontab -u jenkins -l |
-l | -u USER | List USER | sudo crontab -u www-data -l |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
sudo crontab -u jenkins -l | CI user schedule | Agents |
crontab -l > backup.cron | Backup before edit | |
crontab backup.cron | Install from file | Restore |
Install from file: crontab /path/to/file replaces the whole crontab.
System cron locations
Definition: System-wide cron drop-ins and run-parts directories under
/etc.
| Path | Meaning |
|---|---|
/etc/crontab | System crontab (has USER column) |
/etc/cron.d/* | Drop-in snippets (USER column) |
/etc/cron.hourly | run-parts hourly. No dots in names (job OK, job.sh skipped) |
/etc/cron.daily | Daily (often via anacron — not a fixed clock time) |
/etc/cron.weekly | Weekly (same anacron caveat) |
/etc/cron.monthly | Monthly |
/var/spool/cron/crontabs/USER | User spool (distro path varies) |
/etc/cron.allow / cron.deny | Who may use crontab |
/etc/crontab line shape: m h dom mon dow USER command — six fields before the command (USER extra).
Cron environment
Definition: How cron jobs get PATH, logging, and flock single-instance guards.
| Pattern | Meaning | Example |
|---|---|---|
| Absolute path | Don’t rely on PATH | /usr/bin/python3 /opt/job.py |
| Redirect | Capture stdout/stderr | ... >>/var/log/job.log 2>&1 |
MAILTO= | Email output to address | MAILTO=ops@corp at top of crontab |
MAILTO="" | Disable mail | Avoid mail spam |
PATH= | Set PATH in crontab (default is often just /usr/bin:/bin) | PATH=/usr/local/bin:/usr/bin:/bin |
SHELL= | Shell for jobs (default /bin/sh, not bash) | SHELL=/bin/bash |
flock | Single instance. Prefer /var/lock or /run/lock over /tmp | flock -n /var/lock/job.lock /opt/job.sh |
flock option | Argument | Meaning | Example |
|---|---|---|---|
-n | — | Fail immediately if lock held | Non-blocking skip |
-w | SEC | Wait up to SEC for lock | flock -w 10 ... |
-c | CMD | Run CMD under lock | flock -n /tmp/l.lock -c cmd |
| (none) | LOCKFILE CMD | Run CMD with lock held | flock /tmp/l.lock cmd |
Cron service
Definition: Check/restart the cron daemon and read its logs.
| Command | Argument | Meaning | Example |
|---|---|---|---|
systemctl status cron | — | Debian/Ubuntu service name | systemctl status cron |
systemctl status crond | — | RHEL family name | systemctl status crond |
systemctl restart cron | — | After major changes (rare need) | |
journalctl -u cron | — | Service logs | journalctl -u cron -n 50 |
grep CRON /var/log/syslog | — | Classic syslog lines | Ubuntu |
journalctl -t CRON | — | Identifier filter |
systemd timers
Definition: systemd calendar or monotonic schedules that start a service (often
Type=oneshot, not required).
| Command | Argument | Meaning | Example |
|---|---|---|---|
systemctl list-timers | — | Active timers + next/last | systemctl list-timers |
systemctl list-timers --all | — | Include inactive | systemctl list-timers --all |
systemctl status | UNIT.timer | Timer unit state | systemctl status logrotate.timer |
systemctl status | UNIT.service | Oneshot service triggered | systemctl status logrotate.service |
systemctl start | UNIT.service | Run job now (manual) | sudo systemctl start backup.service |
systemctl start | UNIT.timer | Enable counting (if stopped) | |
systemctl enable --now | UNIT.timer | Boot + start timer | sudo systemctl enable --now backup.timer |
systemctl cat | UNIT.timer | See OnCalendar etc. | systemctl cat backup.timer |
systemd-analyze calendar | EXPR | Validate calendar expr | systemd-analyze calendar '*-*-* 02:05:00' |
journalctl -u | UNIT.service | Logs for the job | journalctl -u backup.service -n 50 |
Common timer directives
| Directive | Argument | Meaning | Example |
|---|---|---|---|
OnCalendar= | expr | Calendar schedule | OnCalendar=*-*-* 02:05:00 |
OnBootSec= | time | After boot | OnBootSec=15min |
OnUnitActiveSec= | time | After the service last started (not when it finished — that is OnUnitInactiveSec=) | |
Persistent= | true | Catch up missed OnCalendar= runs after downtime. No effect on monotonic timers | Laptops/agents |
RandomizedDelaySec= | time | Jitter (stampede control) | Fleet |
Unit= | service | Which service to run | Default name match |
Flag combos
| Combo | Meaning | Example |
|---|---|---|
systemctl list-timers --all | head | What’s scheduled | Morning check |
systemctl start foo.service | Manual catch-up | Don’t wait for calendar |
journalctl -u foo.service --since today | Did it run? |
Debug cron
Definition: Checklist for silent cron failures (env, user, locks, logs).
| Step | Command / action | Notes |
|---|---|---|
| 1 | systemctl status cron | Daemon up? |
| 2 | crontab -l / sudo crontab -u U -l | Job still there? |
| 3 | Check logs | journalctl -u cron / syslog |
| 4 | env -i HOME="$HOME" PATH=/usr/bin:/bin /bin/sh /path/job.sh | Sparse env + sh, not bash |
| 5 | Permissions on script + dirs | ls -l script |
| 6 | Lock file stuck? | fuser /tmp/job.lock |
| 7 | SELinux/AppArmor only if enforcing | Advanced |
Common recipes
| Goal | Command |
|---|---|
| List my cron | crontab -l |
| Edit my cron | crontab -e |
| List jenkins cron | sudo crontab -u jenkins -l |
| Daily 02:05 job line | 5 2 * * * /opt/job.sh >>/var/log/job.log 2>&1 |
| Every 5 minutes | */5 * * * * /opt/check.sh >>/var/log/check.log 2>&1 |
| Locked single-run | */5 * * * * /usr/bin/flock -n /var/lock/j.lock /opt/job.sh |
| List timers | systemctl list-timers --all |
| Run timer’s service now | sudo systemctl start myjob.service |
| Validate calendar | systemd-analyze calendar 'Mon *-*-* 09:00:00' |
Pitfalls
- PATH is minimal (
/usr/bin:/bin) and SHELL is/bin/sh. Bashisms and barepythonfail. Use absolute paths or setPATH=/SHELL=in the crontab. %in a cron command is a newline (rest becomes stdin) unless written\%.date +%Yis a classic silent break.- Comments are not allowed on the same line as a job.
- Output with no redirect is mailed to the owner (
MAILTO=). EmptyMAILTO=""or log to a file. No MTA → output often vanishes. crontab -rdeletes all entries.crontab -eas root edits root’s crontab, not/etc/crontab.- DOM and DOW both restricted → OR (Vixie/cronie).
0 9 1 * 1is the 1st or Mondays, not “first Monday.” systemdOnCalendar=is not that OR. - User crontab has 5 time fields;
/etc/crontaband/etc/cron.d/*insert a USER field. Files incron.d/cron.dailywith a dot in the name are often ignored (run-parts). - Debian/Ubuntu
cron.dailyoften goes through anacron — “daily” ≠ 00:00 sharp. - DST: the skipped hour never matches; the repeated hour can run twice.
- Overlapping long jobs without
flockpile up. Put lock files in/run/lockor/var/lock, not world-writable/tmp. - Enable the
.timer, not usually the.service(or the job also starts at boot).AccuracySec=defaults to 1 min, so timers are not second-exact. Persistent=only catches up calendar timers. Checklist-timersLAST/NEXT.
For more details, try man <command> in your terminal.