Back to blog

Reference · April 6, 2026

Where are cron logs stored on Linux (and how to actually find yours)

Cron does not have its own log file. It writes through syslog and the path depends on the distro. The exact paths for Debian, RHEL, Alpine, macOS, and systemd, plus when to stop reading log files.
crontap.com / blog
Cron does not have its own log file by default. It writes through syslog, and which file your cron logs land in depends on the distro. Path-by-path reference for Debian, Ubuntu, RHEL, Alpine, macOS, and systemd journals.

Cron does not have its own log file by default. It writes through syslog. Which file your cron logs land in depends on which distro you are on, which init system, and whether anyone has touched the rsyslog config. This is the actual path-by-path reference, plus when you should stop reading log files and start using a managed dashboard.

TL;DR by distro:

  • Debian / Ubuntu: /var/log/syslog (filtered) and journalctl -u cron.
  • RHEL / CentOS / Rocky / Fedora: /var/log/cron (dedicated file).
  • Alpine, Busybox: /var/log/messages (or stdout if no syslogd).
  • macOS: log show --predicate 'process == "cron"' --last 1h.
  • systemd-only systems: journalctl -u cron or journalctl -u crond.

If you are mid-incident and the log file is silent, see cron job not running: the 8-step debug checklist. For the full troubleshooting decision tree, see the cron troubleshooting hub.

Debian and Ubuntu

The cron daemon on Debian-family distros (Debian, Ubuntu, Linux Mint) writes through syslog. The lines land in /var/log/syslog interleaved with everything else syslog is collecting:

grep CRON /var/log/syslog

For just the last hour:

grep CRON /var/log/syslog | tail -50

On modern Ubuntu (20.04+), rsyslog is the default and the file is still /var/log/syslog. On systems where someone disabled rsyslog in favor of systemd-journald only, the file may not exist. In that case use:

journalctl -u cron --since today

journalctl is the right answer on any Debian/Ubuntu system with systemd. It works even when rsyslog is degraded.

What the line looks like

A typical cron line in /var/log/syslog:

May 24 02:00:01 server CRON[12345]: (root) CMD (/usr/bin/python3 /opt/sync.py)

The format is timestamp, hostname, process name with PID, then (user) and CMD (command). If the command produced output and was not redirected, you may also see a separate line from (CRON) with the stdout / stderr the job emitted, sent to syslog priority info.

RHEL, CentOS, Rocky Linux, AlmaLinux, Fedora

Red Hat family distros have a dedicated cron log file at /var/log/cron:

tail -100 /var/log/cron

This is configured in /etc/rsyslog.conf (or /etc/rsyslog.d/) with a line like:

cron.*    /var/log/cron

Logrotate handles rotation; by default the file rotates weekly and keeps 4 weeks of history.

To follow live:

sudo tail -F /var/log/cron

journalctl -u crond also works on systemd-based RHEL versions (7+).

What the line looks like

May 24 02:00:01 server crond[12345]: (root) CMD (/usr/bin/python3 /opt/sync.py)

Same shape as Debian, slightly different process name (crond instead of CRON).

Alpine and Busybox

Alpine ships busybox crond rather than vixie-cron. Logging behavior depends on whether syslogd is running:

# Check if syslog is running on Alpine
rc-status | grep syslog

If syslog is up, cron logs land in /var/log/messages:

grep crond /var/log/messages

If syslog is down (common in minimal containers), cron writes to stderr, which goes nowhere unless you started crond with -f -L 8 (foreground, log level 8) and redirected output. In production Alpine containers, the usual pattern is to run crond in the foreground as PID 1 with explicit log redirection:

CMD ["crond", "-f", "-d", "8"]

The -d 8 enables debug-level output to stderr; Docker then captures stderr and you read it with docker logs <container>.

For more on cron inside containers, see Docker cron jobs.

macOS

macOS still ships cron, though Apple has pushed launchd as the modern alternative since 10.4. If you are using crontab -e on macOS, the cron daemon is logging through the Unified Logging system. The legacy /var/log/system.log was retired in macOS Sierra; the modern command is:

log show --predicate 'process == "cron"' --last 1h

For live tailing:

log stream --predicate 'process == "cron"'

If your job uses any GUI-bound API (AppleScript that talks to the desktop session, anything that wants the user keychain), cron may run but the side effects fail silently. That is a launchd use case, not a cron one.

systemd journals (any distro)

On any systemd-based distro, the universal command is:

journalctl -u cron
# or:
journalctl -u crond

Useful flags:

  • --since "1 hour ago" to scope to recent events.
  • -f to follow live.
  • -x to include explanatory messages from the unit.
  • --user if you are looking at user-level systemd timers (not classic cron, but adjacent).

systemd timers are a popular alternative to cron on modern Linux. If your team migrated to *.timer units, journalctl -u myjob.timer and journalctl -u myjob.service are where the logs are.

When the cron log is silent

If grep CRON /var/log/syslog (or the distro equivalent) shows nothing at the expected minute, the daemon did not attempt to run the job. The usual causes:

  1. The cron daemon is not running. Check with systemctl status cron or systemctl status crond.
  2. The crontab entry is in a different user's crontab. Cron will not show up under your user if root owns the entry.
  3. The crontab syntax is invalid and the daemon refused to load the file. Validate in the free cron expression debugger.
  4. The server's clock is wrong. Run timedatectl and verify the timezone matches what you wrote in the crontab.

The full debug walkthrough is in cron job not running: the 8-step checklist.

When the log shows the job ran but nothing happened

The log line proves cron called your command. It does not prove your command did anything. Common silent failures:

  1. The command exited 0 without doing work. Wrong env vars, missing DB connection string, a feature flag that turned off the code path. Compare manual run env vs cron run env: add env > /tmp/cron-env.txt to the job and diff.
  2. The command's output went to /dev/null. By default cron emails the crontab owner with any output; many crontabs end in >/dev/null 2>&1 to suppress that. Remove the redirect during debugging.
  3. Cron used the wrong shell. Default is /bin/sh, which is dash on Debian/Ubuntu and lacks bashisms. Set SHELL=/bin/bash at the top of the crontab if your script uses bash features.

When you should not be reading cron logs

You are reading cron logs because something failed and you want to know why. That is fine for a one-off incident. If you find yourself in /var/log/cron more than once a month, the structural answer is to stop reading log files and start using a dashboard that tells you which run failed, when, and what the response was.

Crontap stores the response body, headers, and timing for every fire. You search the dashboard, not grep. Failed runs trigger an email / Slack / Discord / Telegram alert within seconds. The free tier covers one schedule end to end; Pro is a flat annual fee for unlimited schedules at minute cadence.

Stop grepping syslog. External cron with stored run logs and failure alerts. Free forever tier, one schedule. Try Crontap →

FAQ

Where is the cron log file on Ubuntu 24.04?

/var/log/syslog if rsyslog is installed (the default). journalctl -u cron if the system relies on systemd-journald only. Both work; pick whichever is convenient.

Why is there no /var/log/cron on Debian?

Debian-family distros do not configure a dedicated cron log file by default. The cron facility goes to syslog and is filtered into /var/log/syslog along with the rest of the system messages. RHEL-family distros configure a dedicated /var/log/cron instead.

How do I see what a cron job printed to stdout?

By default, cron emails the crontab owner with any stdout/stderr the job produced. If the system does not have a mailer (most containers do not), that mail goes nowhere. The reliable pattern is to redirect explicitly in the crontab:

0 2 * * * /opt/sync.py >> /var/log/sync.log 2>&1

Or, if you want the output to be queryable: have the job write to your structured logging service (Datadog, CloudWatch, etc.) and stop relying on cron's mail.

How do I get an alert when a cron job fails?

Either wire stderr into your existing alerting (writes to your log aggregator with a level that pages on-call), or move the cron to a managed external scheduler that includes failure alerts. Crontap retries on 5xx and emails the crontab owner with the response body and status code. Detailed pattern in cron job monitoring.

Are systemd timers better than cron for logging?

For logging, yes. systemd timers run their service unit on a schedule, and the service unit logs to the journal with structured metadata (exit code, duration, stdout, stderr). journalctl -u myjob.service gives you a clean per-run timeline. The trade-off is that systemd timers are more verbose to set up than a crontab line.

Related on Crontap

Stop grepping syslog. External cron with stored run logs and failure alerts. Free forever tier, one schedule. Try Crontap →

From the blog

Read the blog

Guides, patterns and product updates.

Tutorials on scheduling API calls, webhooks and automations, plus deep dives into cron syntax, timezones and reliability.

Alternatives

Vercel Cron every minute: beating the Hobby hourly limit

Vercel Cron caps Hobby at hourly cadence and 5 jobs, and ties every change to a redeploy. Here is the external cron pattern teams use to ship per-minute schedules, per-IANA timezones, and one dashboard across projects without paying $20/mo per user for Pro.

Alternatives

Cloud Run cron without Cloud Scheduler

Cloud Scheduler costs $0.10 per job per month after the first 3 and asks for OIDC plus IAM bindings on every target. Here is the IAM-free pattern Cloud Run teams use to fire their .run.app URLs on a clock with one bearer token and one dashboard across every GCP project.