cron, one lesson per page
Twenty-nine lessons for scheduling on Linux and Unix: how crontab's five fields really match, the DOM/DOW OR rule that surprises everyone, ranges, lists and step values, the special @ strings, the environment every job runs in (a sparse PATH and /bin/sh), why a job runs in your terminal but never under cron, the % that becomes a newline, cron.d files, anacron for machines that sleep, the cron.deny and cron.allow gates, and the logging and mail habits that show what actually ran.
A diagram, the schedule, and one thing to try this week. That's a page.
cron, one lesson per page
Twenty-nine lessons for scheduling on Linux and Unix: how crontab's five fields really match, the DOM/DOW OR rule that surprises everyone, ranges, lists and step values, the special @ strings, the environment every job runs in (a sparse PATH and /bin/sh), why a job runs in your terminal but never under cron, the % that becomes a newline, cron.d files, anacron for machines that sleep, the cron.deny and cron.allow gates, and the logging and mail habits that show what actually ran.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
Every fact in this book is as the official sources state it, fetched and read during this build: the crontab(5), cron(8) and crontab(1) man pages hosted at man7.org (the cronie crond project), the Debian crontab(5) man page (classic Vixie lineage), and the Debian anacron(8) and anacrontab(5) man pages. The Wikipedia crontab article was read for orientation only and no facts are sourced from it. cronie-specific extensions are labelled as the sources label them. Demand evidence from live beginner threads, reconfirmed at dispatch; no facts are sourced from Reddit or StackExchange. An independent guide, not affiliated with or endorsed by the cronie project or the Debian Project.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodgkiss.net
Contents
The line
The five time-and-date fields and the command, checked once a minute; asterisks, ranges, lists and steps; the OR rule for the two day fields; names and the @ special strings.
- 01Five fields, one line
- 02The OR rule
Per crontab(5): each line has five time-and-date fields followed by a username (if this is the system crontab file) and followed by a command; commands are executed by cron(8) when the minute, hour and month of year fields match the current time and at least one of the two day fields (day of month, or day of week) matches the current time; cron(8) examines cron entries every minute; the fields are minute 0-59, hour 0-23, day of month 1-31, month 1-12 (or names), day of week 0-7 (0 or 7 is Sunday, or use names).
Five fields, one line
Every job is one line: five time fields, then the command. Minute, hour, day of month, month, day of week. In the system crontab a username sits between the fields and the command.
cron wakes once every minute and runs the command when the minute, hour and month match and at least one of the two day fields matches. Nothing else exists: no seconds, no years.
Learn the five slots and you can read any crontab on any machine.
Write one line this week, 30 4 * * * in front of a command you can see happen, and watch it fire at 4:30 in the morning.
Per crontab(5), Note: the day of a command's execution can be specified by two fields, day of month and day of week; if both fields are restricted (i.e. do not contain the * character), the command will be run when either field matches the current time; for example 30 4 1,15 * 5 would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.
The OR rule
The trap behind every scheduler question: when both day fields are restricted, no asterisk in either, cron ORs them. 30 4 1,15 * 5 fires on the 1st and 15th plus every Friday, not only on Fridays that are the 1st.
When you need AND, leave one day field as an asterisk, or do the date test inside the command itself.
Two restricted day fields is one OR. Everyone meets this rule the hard way.
Test it safely this week: put 30 4 1,15 * 5 echo in a scratch crontab and check a calendar for which Fridays double up.
Per cron(8): the -P option tells crond not to set PATH, PATH is instead inherited from the environment of the daemon, so by default crond sets a PATH of its own for the jobs it runs; per crontab(5), environment settings of the form name = value are allowed in the crontab and apply to the jobs that follow them.
A sparse PATH
The classic break: your terminal carries your login PATH, the job does not. crond sets a PATH of its own, inherited from the daemon only when crond runs with -P.
The manual's answer is boring and total: write absolute paths, /usr/local/bin/backup rather than backup, or set PATH once at the top of the crontab.
A command that is not found under cron is a PATH question nine times out of ten.
Take one failing job this week and give its command the full absolute path. Then add a PATH line at the top of the crontab and retire the whole class of failure.
System jobs
crontab -e is only one of three homes: the spool, /etc/crontab and /etc/cron.d with its username field and stricter file rules, and the run-parts directories it feeds.
- 01Where jobs live
- 02Jobs in /etc/cron.d
Per cron(8): crond searches /var/spool/cron for crontab files which are named after accounts in /etc/passwd; it also searches /etc/anacrontab and any files in the /etc/cron.d directory, which have a different format; /etc/crontab is the system crontab; per crontab(1), the spool files are not intended to be edited directly, crontab -e installs them.
Where jobs live
Three homes, one daemon: /var/spool/cron holds per-user tables named after accounts, while /etc/crontab and /etc/cron.d hold system jobs.
The spool is not for hand editing: install through crontab -e, which handles the file, the permissions and the reload.
User jobs go through crontab, system jobs go in files. The daemon reads both every minute.
List your own table this week with crontab -l, then list /etc/cron.d and count how many packages shipped jobs there.
Per crontab(5), Jobs in /etc/cron.d: the jobs in cron.d and /etc/crontab are system jobs, used usually for more than one user, thus additionally the username is needed; the example job in /etc/cron.d is MAILTO=root then * * * * * root touch /tmp/file; per the CAVEATS, the files must be regular files, not executable and not writable for anyone else but the owner, and each entry must end in a newline.
Jobs in /etc/cron.d
A cron.d file is a crontab with one extra field: the username, sitting between the schedule and the command. The manual's example is five stars, root, touch /tmp/file.
The discipline is higher here: regular files only, no group or world write, and the final-newline law applies. Drop a file in and cron reads it within the minute.
Packages own /etc/cron.d, root edits it, users never need to.
Read one shipped file in /etc/cron.d this week and find the username column. That one field is the whole difference.
Per crontab(5), the EXAMPLE CRON FILE: 5 0 * * * with the command $HOME/bin/daily.job redirected with >> to $HOME/tmp/out with 2>&1; per crontab(1), crontab -l displays the current crontab; per cron(8), any output is mailed to the owner or MAILTO, and job output can be sent to syslog with the crond -s option.
One habit to keep
The manual's own example ends with the habit: the job, then a redirect into a log, both streams. One file, both streams, a schedule you already trust.
Then read it: a log nobody opens is mail nobody reads. Pair it with crontab -l kept somewhere safe and the syslog trace.
Absolute paths, escaped percents, a final newline, a log you read. That is the whole religion.
Add output redirection to every line you own this week. One append per line, and cron finally reports to you.