Cron Implementations Compared: cronie, Debian, BusyBox, fcron
Contents
Linux has no single cron. Debian and Ubuntu run Debian's patched Vixie cron 3.0pl1, Fedora and RHEL run cronie, Alpine and many minimal images run BusyBox crond, and containers often run supercronic. To see which one you have, run pgrep -a cron and ask the package manager which package owns that binary. They all read five-field crontab lines but differ on which variables they honor, CRON_TZ, daylight saving time, % in commands, whether both day fields must match, and where job output goes. Everything below comes from each project's source and manuals: cronie 1.7.2, Debian's cron 3.0pl1-197, BusyBox 1.37.0, fcron 3.4.1, supercronic 0.2.51, and systemd-cron 2.8.2, with package versions checked October 25, 2026.
Find Out Which Cron You Are Running#
Start with the process list. The unit list matters because systemd-cron has no daemon to find:
# PID and command line of every process whose name contains "cron"
pgrep -a cron
# systemd hosts: installed units with "cron" in their name
systemctl list-unit-files '*cron*'
# Which package owns the binary
dpkg -S /usr/sbin/cron # Debian, Ubuntu: prints "cron: /usr/sbin/cron"
rpm -qf /usr/sbin/crond # Fedora, RHEL: prints the cronie package
pacman -Qo /usr/bin/crond # Arch
readlink -f /usr/sbin/crond # Alpine: /bin/busybox means BusyBox crondpgrep -a cron shows |
Unit | Implementation | Version |
|---|---|---|---|
/usr/sbin/cron -f |
cron.service |
Debian cron | dpkg-query -W cron |
/usr/sbin/cron -f -P |
cron.service |
Debian cron on Ubuntu 24.04 or later | dpkg-query -W cron |
/usr/sbin/crond -n |
crond.service; cronie.service on Arch |
cronie | crond -V |
/usr/sbin/crond -c /etc/crontabs -f |
None; Alpine uses OpenRC | BusyBox crond, if readlink confirms |
First line of busybox output |
fcron -f |
fcron.service |
fcron | fcron -V |
supercronic and a crontab path |
Usually none; it's a container's main process | supercronic | supercronic -version |
| Nothing | cron.target |
systemd-cron | Package manager |
Command lines and unit names come from each package's service file (Debian, Ubuntu, cronie, Arch, Alpine). cronie, which Alpine also packages, names its daemon crond too, hence the readlink check.
Where Each Implementation Comes From#
cronie, Debian's cron, and macOS's cron descend from Paul Vixie's cron. Debian still builds version 3.0pl1 from the early 1990s (debian/copyright) with 85 patches in trixie's 3.0pl1-197 (series); the 197 is the Debian revision, not a patch count. ISC's last release, cron 4.1, is a January 2004 file on ftp.isc.org, and cronie's history starts in 2007 with a commit titled "Source files of vixie-cron-4.1". macOS ships Apple's build of FreeBSD's Vixie cron (source).
BusyBox crond is "a port of dcron", Matt Dillon's cron (crond.c), and dcron itself survives as dcron 4.6 in Alpine's community repository. fcron is an independent C scheduler, supercronic a Go program from Aptible, and systemd-cron a converter that hands crontabs to systemd. A history of cron covers the earlier lineage, back to Sixth Edition Unix in 1975.
How They Differ, Side by Side#
Crontab syntax:
| cronie 1.7.2 | Debian cron 3.0pl1 | BusyBox 1.37.0 | fcron 3.4.1 | supercronic 0.2.51 | systemd-cron 2.8.2 | |
|---|---|---|---|---|---|---|
| Both day fields restricted | Either must match | Either | Either | Both, unless dayor |
Either | Both |
7 as Sunday |
Yes | Yes | No | Yes | Yes | Yes |
% in a command |
Newline; rest is stdin | Same as cronie | No special meaning | No special meaning | No special meaning | Same as cronie |
# after a command |
Part of the command | Part of the command | Starts a comment | Part of the command | Part of the command | Part of the command |
@reboot |
Yes | Yes | Yes | Yes | No | Yes, as OnBootSec= |
| Seconds field | No | No | No | No | Yes, in the seven-field form | No |
| Variable lines | Any, for lines below | Any, for lines below | MAILTO, SHELL, PATH only |
Any, plus ! option lines |
Any, applied to the whole file | Any, for lines below |
| Schedule time zone | CRON_TZ, per entry |
System zone only | crond's zone only | timezone() option |
CRON_TZ, one per file |
CRON_TZ or TZ |
Running jobs:
| cronie | Debian cron | BusyBox | fcron | supercronic | systemd-cron | |
|---|---|---|---|---|---|---|
| Daylight saving time | Skipped hour runs after the jump; repeated hour once, system zone only | Same as cronie | No handling | Not documented in detail | Not documented | systemd calendar rules |
| Runs missed while down | No; anacron runs the daily directories | No | Only across a forward clock jump of up to an hour | bootrun; @ lines count uptime |
No | @daily-style jobs, at boot |
| Previous run still going | Starts another | Starts another | Skips | Skips, unless exesev |
Skips and warns | Not started again |
| Job output | Mailed; syslog when sendmail is missing | Mailed; discarded with no MTA | Mailed only with MAILTO, else crond's stdout |
Mailed | Logged to stderr | Journal; mail on failure |
| Daemon log | syslog CMD and CMDEND lines |
syslog, chosen with -L |
syslog, a file, or stderr | syslog or a file | stderr, text or JSON | Journal |
| Crontab locations | /var/spool/cron/<user>, /etc/crontab, /etc/cron.d |
/var/spool/cron/crontabs/<user>, /etc/crontab, /etc/cron.d |
One directory; /etc/crontabs/<user> on Alpine |
fcron's spool, via fcrontab |
The file you pass it | /etc/crontab, /etc/cron.d, the user spool |
Day Fields: Some Crons Match Either, Some Match Both#
When the day-of-month and day-of-week fields are both restricted, POSIX says a day matching either one matches. Not every implementation agrees, and it changes what a common attempt at "first Monday of the month" does:
0 9 1-7 * 1 /usr/local/bin/monthly-review.sh| Implementation | Runs on |
|---|---|
| cronie, Debian cron, BusyBox, supercronic, macOS | The 1st through the 7th, and every Monday |
fcron (default dayand), systemd-cron 2.8.2 |
Only Mondays from the 1st to the 7th |
They also disagree on what "restricted" means. cronie and Debian treat a field that starts with * as unrestricted (cronie cron.c, Debian cron.c), so 0 0 */2 * sun runs only on Sundays with odd dates, as Debian's crontab(5) points out. BusyBox and supercronic call a field restricted whenever it leaves out any value (crond.c, cronexpr), so the same line runs every Sunday plus every other date.
For a rule that means the same thing everywhere, restrict one field and test the other in the command:
0 9 1-7 * * [ "$(date +\%u)" = 1 ] && /usr/local/bin/monthly-review.shCronRadar's schedule parser sides with fcron here: when both day fields are restricted, it expects a run only on dates that match both, so a crontab line relying on either-field matching needs two monitors (schedule syntax).
cronie: CRON_TZ, RANDOM_DELAY, and Syslog Without Mail#
cronie 1.7.2 is the cron in Fedora 43 through 45 (packages); CentOS Stream 10 has 1.7.0 and CentOS Stream 9 has 1.5.7 (10, 9). Arch packages 1.7.2 but installs no cron by default (Arch Wiki). Debian and Ubuntu carry it as an alternative package. Beyond the Vixie base, its crontab(5) and release notes add:
CRON_TZ=Europe/Berlin, which sets the zone for matching the entries below it, andRANDOM_DELAY, an upper limit in minutes on a start delay.MAILFROMfor the sender; since 1.7.2 crond also inherits it from its own environment.~for a random value, as in6~15in the minute field, picked when the crontab loads (1.6.0+).- A command that begins with
-nmails its output only when the job fails (1.7.0+), and a root entry that begins with-writes no log line (entry.c, do_command.c, entry.c). - inotify reloading, PAM, and SELinux (cron(8)).
Cron jitter compares RANDOM_DELAY and ~ with other ways to spread start times.
For jobs in the system zone, crond runs fixed-time jobs from a skipped hour right after the jump and doesn't repeat them in a repeated hour; CRON_TZ entries get no such protection (cron.c). Cron and daylight saving time walks through both cases.
When /usr/sbin/sendmail is missing and no -m mailer is set, crond logs job output to syslog as CMDOUT lines instead of mailing it (cron.c). -s does that deliberately and -m off turns mail off. Each run logs CMD at the start and CMDEND at the end, with no exit status (do_command.c). Fedora runs the daily, weekly, and monthly directories through the bundled anacron unless you install cronie-noanacron (cronie.spec). With neither cron.allow nor cron.deny present, only root may use crontab (crontab(1)); Fedora and Arch install a cron.deny that names no one (Arch), which lets every user in.
Debian and Ubuntu cron: Patched Vixie cron 3.0#
Debian 13 ships cron 3.0pl1-197. Ubuntu builds the same package with its own patches: 3.0pl1-184ubuntu2 in 24.04, 3.0pl1-200ubuntu1 in 26.04, and 3.0pl1-209ubuntu1 in 26.10 (Launchpad). Debian's cron(8) lists its additions under DEBIAN SPECIFIC, among them /etc/cron.d, PAM, SELinux, audit logging, and the daylight saving handling, which matches cronie's for every fixed-time entry. What catches people out:
/etc/cron.dfile names may use only letters, digits, underscores, and hyphens, sobackup.cronis ignored. The files must be root-owned and not group- or other-writable, and they don't inherit variables set in/etc/crontab.- There's no
CRON_TZ. Per crontab(5), aTZline changes the commands' environment, not when they run. -Lpicks what is logged, as a sum: 1 job starts (the default), 2 job ends, 4 failed jobs, 8 process IDs. Debian reads extra daemon options fromEXTRA_OPTSin/etc/default/cron; Ubuntu deprecated that file in favor ofsystemctl edit cron.service(cron.default).- Ubuntu 24.04 and later start the daemon with
-P, an Ubuntu patch, so jobs inherit the daemon'sPATH(changelog). Cron environment variables covers what each implementation passes to jobs. - Without a mail transfer agent, output is thrown away and syslog gets
No MTA installed, discarding output(do_command.c).
BusyBox crond: Three Variables and One Directory#
BusyBox 1.37.0 is the crond in Alpine 3.24 (APKBUILD); Alpine's edge branch has moved to 1.38.0, released May 13, 2026 (busybox.net), whose crond also reaps orphaned processes. From crond.c:
- It reads one directory of crontabs, each named after an existing user and owned by root:
/var/spool/cron/crontabsby default. Alpine's service passes-c /etc/crontabs, and/var/spool/cron/crontabsis a symlink to it (alpine-baselayout). There's no/etc/crontaband no/etc/cron.d. - Only
MAILTO=,SHELL=, andPATH=lines count, written without spaces around=; other assignments are skipped without an error (L463-L478). - A
#anywhere ends the command, since the line parser treats it as a comment (parse_config.c), and a trailing\joins the next line (L99-L134).%means nothing special, and a\%written for other crons reaches the shell as typed. 7isn't accepted for Sunday. The field parser can never reach 7 in a seven-day field, logs a parse error, and leaves the field set to every day, so0 9 * * 7runs daily (ParseField).*in the day-of-month field counts from 0, so*/2there selects even dates, where cronie and Debian select odd ones (L239). Write1-31/2for odd dates everywhere.- Output is mailed only when
MAILTOis set; otherwise it goes to crond's own stdout and stderr (L757-L790). crond logs to syslog unless given-L fileor-d level, which writes to stderr; at the default level, 8, each start is logged asUSER ... pid ... cmd. - A line whose previous run is still alive isn't started. A forward clock jump of up to an hour runs the skipped minutes' jobs once; a larger jump either way runs nothing and logs
time disparity(L1093-L1129). Daylight saving time gets no handling at all.
fcron: Catch-Up for Machines That Aren't Always On#
fcron 3.4.1, released June 6, 2026 (fcron.free.fr), is packaged by Arch and, at 3.4.0, Alpine. Users install tables into its spool with fcrontab. It doesn't read /etc/crontab or /etc/cron.d, though its check_system_crontabs script can merge them into root's system table. fcrontab(5) has three kinds of entry:
# &: a normal five-field schedule; bootrun runs it at startup if it was missed while down
&bootrun 30 2 * * * /usr/local/bin/backup.sh
# @: every 12 hours of fcron uptime, with the remaining time saved across shutdowns
@ 12h /usr/local/bin/rotate-cache.sh
# %: once a night, as early as possible between 01:00 and 04:59
%nightly * 1-4 /usr/local/bin/reindex.shOptions follow the &, @, or % prefix, or go on ! lines that apply to every entry below. Besides matching day fields with AND by default, fcron skips a job whose previous run is still going unless exesev is set, sets a job's zone with timezone(Europe/Paris), mails only failures with erroronlymail, and waits 20 seconds after starting before it runs anything, @reboot jobs included. It logs to syslog, or to a file with -p (fcron(8)).
supercronic: A Crontab Runner for Containers#
supercronic 0.2.51 was released October 21, 2026 (release). You hand it one file, supercronic /etc/crontab; there's no user field, no switching users, no spool, and no mail (README).
- Jobs inherit supercronic's whole environment, so
ENVanddocker run -evalues reach them.NAME=valuelines also work, but they go into one set for the whole file: a later line applies to jobs above it too, and the lastCRON_TZsets the zone for every job (crontab.go). - It accepts five fields, six (a year appended), or seven (seconds first, year last), so
*/15 * * * * * *runs every 15 seconds. Its bundled cronexpr also acceptsL,W, and#, and doesn't support@reboot(cronexpr). - Each output line is logged to stderr with the job's schedule and command.
-split-logssends info lines to stdout,-jsonswitches to JSON, and-passthrough-logsprints job output unwrapped. - A job isn't started while its previous run is going, and supercronic warns that it's late, unless you pass
-overlapping. SIGTERM, SIGINT, and SIGQUIT wait for running jobs before exiting; SIGUSR2, or a file change with-inotify, reloads the crontab. -testchecks the file and exits,-prometheus-listen-addressserves run metrics, and-sentry-dsnreports errors to Sentry (main.go).
Cron in Docker has a working supercronic image.
systemd-cron: Crontabs Turned Into Timers#
systemd-cron runs no daemon. A systemd generator turns /etc/crontab, /etc/cron.d, /etc/anacrontab, and user crontabs into .timer and .service units whenever systemd reloads, and a path unit triggers that reload when the files change (systemd.cron(7)). Version 2.8.2, released July 5, 2026, is in Debian forky and Ubuntu 26.10; Debian 13 has 2.5.1 (Debian). Enable it with systemctl enable --now cron.target.
Jobs take on systemd's behavior: output goes to the journal, a timer doesn't start a service that's still running (systemd.timer), and schedules follow systemd's calendar rules. Its crontab(5) adds PERSISTENT (by default only @daily-style jobs run at boot after a missed run), RANDOM_DELAY, DELAY in minutes after boot, CRON_TZ, and CRON_MAIL_SUCCESS. Mail goes out for every failed run and, by default, for successful runs that printed something.
The day-field rule in its man page isn't what the generator does. crontab(5) says either field may match, but the generator writes both into one OnCalendar= line (generator), and systemd requires every part of a calendar event to match (systemd.time(7)). Its own test expects 1 2 3 4 5 to become Fri *-4-3 2:1:00 (test-generator), which fires only when April 3 is a Friday. systemd timers vs cron explains the calendar syntax.
macOS: Vixie cron Behind launchd#
launchd starts /usr/sbin/cron only when /etc/crontab or a file in /usr/lib/cron/tabs exists, and it passes no options (com.vix.cron.plist), so the -s flag that turns on daylight saving handling is off (cron(8)). Apple's documentation says cron "has been deprecated in favor of launchd" and that cron jobs due while the Mac is asleep or off wait for the next scheduled time (Apple). Cron on macOS covers moving jobs to launchd.
Frequently Asked Questions#
Which cron does Ubuntu use?#
Ubuntu uses Debian's cron package, Vixie cron 3.0pl1 with Debian's patches and a few of Ubuntu's: 3.0pl1-184ubuntu2 in 24.04 LTS and 3.0pl1-200ubuntu1 in 26.04 LTS. Since 24.04 its service starts the daemon with -P, so jobs inherit the daemon's PATH. cronie and systemd-cron are in the universe repository, and the cron package conflicts with both (control), so installing either removes it.
What is cronie?#
cronie is the cron in Fedora, RHEL and its rebuilds, and Arch's repositories, started in 2007 from the Vixie cron 4.1 source. It adds PAM and SELinux support, inotify reloading, CRON_TZ, RANDOM_DELAY, and a bundled anacron. crond -V prints its version.
What is the difference between cron and crond?#
Usually only the name. Both are the daemon: Debian, Ubuntu, and macOS call it cron, while cronie and BusyBox call it crond, and the service names follow (cron.service on Debian and Ubuntu, crond.service on Fedora and RHEL). The behavior differences come from the implementation behind the name.
Does BusyBox crond read /etc/cron.d?#
No. BusyBox crond reads one directory of per-user crontabs, /etc/crontabs on Alpine, and ignores both /etc/crontab and /etc/cron.d. Put system jobs in /etc/crontabs/root, or drop a script into one of Alpine's /etc/periodic directories, which root's default crontab runs with run-parts (crontab).
When should I use supercronic?#
Use it when cron runs inside a container as the main process and you want jobs to see the container's environment, output in docker logs, and a clean shutdown on SIGTERM; supercronic does all three by default. On a shared server, cronie or Debian's cron fits better, because supercronic can't run jobs as different users and doesn't mail output.
Put this guide to work
Know when the next run is missing
Connect one scheduled job, confirm a real completion, then verify the alert destination. Keep your existing scheduler in charge of running the work.