Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Operations

crontab logs: Where They Go, How to Find Them, and Why the Job Ran but the Output Is Nowhere

Sean

Platform Writer

Jun 18, 2026
6 min read

The headline answer to “where are cron logs” is “they go to a log file or your local mailbox.” That is what most teams discover the first time a cron job silently fails. The honest version covers where cron sends output by default, where the cron daemon’s logs live on each distro, how to capture stdout and stderr to a real log file, how to log to syslog from inside a cron job, and the four failure modes that look like “the cron job ran but nothing happened.”

crontab logs: Where They Go, How to Find Them, and Why the Job Ran but the Output Is Nowhere

Table of contents

The short version: cron jobs run via a shell that does not have a TTY, so stdout/stderr do not appear in your terminal. By default, cron emails the output to the user that owns the crontab — usually root. On most modern distros, that mail is dropped because there is no MTA. The output is lost. The fix: redirect stdout and stderr to a log file, rotate that file, and have a real monitoring strategy for “did the job run.”

crontab logs: where they go, how to find them, and why the job ran but the output is nowhere

Table of contents

The direct answer

The three things to set up for debuggable cron jobs:

# 1. Send all output to a log file
0 2 * * * /usr/local/bin/backup.sh >> /var/log/myapp/backup.log 2>&1

# 2. Rotate the log file
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
  daily
  rotate 14
  compress
  missingok
  notifempty
}

# 3. Monitor that the job ran
# Add to cron entry: a heartbeat write to a database or a metrics system

That is the minimum. The cron daemon’s own logs are in /var/log/cron (RHEL-family), /var/log/syslog (Debian-family), or journalctl -u cron (systemd distros).

Where cron daemon logs live (per distro)

The cron daemon writes to the system log when it runs jobs. The location varies:

RHEL / CentOS / Fedora / Rocky / Alma:

  • /var/log/cron — daemon-level events (job started, job finished, errors).
  • journalctl -u crond — systemd journal.

Debian / Ubuntu:

  • /var/log/syslog — daemon events mixed with other system logs.
  • journalctl -u cron — systemd journal.

Amazon Linux:

  • /var/log/cron — daemon-level events.
  • journalctl -u crond — systemd journal.

Alpine / minimal Docker:

  • Often no syslog daemon. Use journalctl if systemd is present, otherwise check /var/log/messages or /var/log/cron.

The daemon logs show when each job started, when it finished, and any errors the daemon saw (missing executable, syntax errors in the crontab). They do not show the job’s stdout or stderr — that goes elsewhere.

Where cron job output goes by default

When a cron job runs, the shell captures its stdout and stderr. If the crontab has no redirect, the captured output is emailed to the user that owns the crontab.

On a server without an MTA (most modern containers and cloud VMs), the mail fails. The output is lost. The cron job appears to “run but do nothing.”

The diagnostic:

# Check if mail is queued
mailq
# or
postqueue -p

# Check the local mailbox (rare but possible)
cat /var/mail/root | less

If mailq shows messages, cron is generating output and the mail subsystem is just not delivering. The fix is either to install an MTA (postfix, sendmail) or to redirect to a log file.

The redirect pattern that captures everything

The minimum useful crontab line:

0 2 * * * /usr/local/bin/backup.sh >> /var/log/myapp/backup.log 2>&1

That redirects stdout to backup.log (with >>, appending), redirects stderr to stdout (2>&1), and the result is one log file with everything.

For timestamped output, wrap the command in a subshell:

0 2 * * * /bin/bash -c '/usr/local/bin/backup.sh 2>&1 | while IFS= read -r line; do echo "$(date -Iseconds) $line"; done >> /var/log/myapp/backup.log'

That prefixes every line with an ISO timestamp. More readable when you tail the log later.

For separate stdout and stderr:

0 2 * * * /usr/local/bin/backup.sh >> /var/log/myapp/backup.log 2>> /var/log/myapp/backup.err

Use this when you want stdout in one file and stderr in another. Most teams use the combined pattern (2>&1) for simplicity.

The logger pattern for syslog integration

If you want the cron output to go through syslog (so it ends up in your centralized logging), use logger:

0 2 * * * /usr/local/bin/backup.sh 2>&1 | logger -t backup -p user.info

The -t backup tags every line with backup:. The -p user.info sets the syslog priority to user.info. The output goes to syslog, which routes it according to the syslog config (usually /var/log/messages or /var/log/syslog).

For structured logging:

0 2 * * * /usr/local/bin/backup.sh 2>&1 | logger -t backup --priority user.info --rfc5424

--rfc5424 enables RFC 5424 structured logging. Some log aggregators parse this.

For JSON:

0 2 * * * /usr/local/bin/backup.sh 2>&1 | while IFS= read -r line; do echo "{"ts":"$(date -Iseconds)","msg":"$line"}" | logger -t backup; done

That emits JSON lines through logger. The receiving log aggregator parses the JSON and indexes the fields.

The log rotation that prevents disk fill

A cron job that appends to a log file forever will fill the disk. The fix is logrotate:

# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
  daily
  rotate 14
  compress
  missingok
  notifempty
  create 0644 root root
  sharedscripts
  postrotate
    # Optional: send SIGHUP to a daemon that has the file open
  endscript
}

The directives:

  • daily — rotate once per day.
  • rotate 14 — keep 14 days of compressed archives.
  • compress — gzip the rotated files.
  • missingok — do not error if the log file is missing.
  • notifempty — do not rotate if the file is empty.
  • create 0644 root root — create a new empty file with these permissions after rotation.

Test the config:

logrotate -d /etc/logrotate.d/myapp

The -d flag is debug mode; it shows what logrotate would do without actually doing it. Once you are confident, remove -d.

For cron jobs that write frequently, also consider using logrotate’s size directive instead of daily — rotate when the file hits 100 MB, for example, regardless of time.

The four failure modes that look like “ran but no output”

  1. The cron daemon never started the job. Check the daemon log (/var/log/cron or journalctl -u cron). Look for “CMD” lines that match your job. If they are not there, cron did not see the entry — usually a typo or wrong user.

  2. The cron job started but the executable was missing. The daemon log shows “cannot execute binary file” or similar. Check the path in the crontab (absolute path, always) and check that the file exists and is executable.

  3. The cron job started, ran, but stdout went to /dev/null. The crontab has > /dev/null or no redirect. The output is lost. Fix: redirect to a log file.

  4. The cron job started, ran, exited non-zero, and the failure went to a mailbox that does not exist. The daemon log shows the job ran. The local mailbox has the failure message. You never saw it because there is no MTA delivering to your inbox. Fix: redirect to a log file and have a monitoring strategy.

The diagnostic for all four: check the daemon log first, then check the redirect target, then check the exit code in the redirect target, then check the cron job itself.

A crontab entry that catches all four:

0 2 * * * /usr/local/bin/backup.sh >> /var/log/myapp/backup.log 2>&1 || echo "[$(date -Iseconds)] BACKUP FAILED with exit $?" >> /var/log/myapp/backup.log

The || ensures that a non-zero exit code writes a failure line to the same log. Now your log file has both the job’s output and a marker if the job failed.

If you are running a cron job on a managed platform like RunxBuild’s backend services, scheduled jobs run as a deployable component with the same logging primitives (stdout/stderr → platform log stream), the same exit-code semantics, and a heartbeat you can monitor without checking a mailbox. For the cost of running scheduled jobs at production scale, the RunxBuild hosting calculator gives you the per-month number.

FAQ

Where is the cron daemon log?

/var/log/cron on RHEL-family, /var/log/syslog on Debian-family. On systemd distros, journalctl -u cron or journalctl -u crond.

Why does my cron job have no output?

By default, cron emails the output to the user that owns the crontab. If there is no MTA, the email is dropped and the output is lost. Fix: redirect to a log file with >> /path/to/log 2>&1.

How do I log to syslog?

Pipe to logger -t <tag> -p <facility>.<level>. The output goes through syslog and ends up wherever syslog is configured to send it.

How do I timestamp each line?

Wrap the command in a subshell that prefixes each line with $(date). Or use ts from the moreutils package if available: command 2>&1 | ts '[%Y-%m-%d %H:%M:%S]' >> /path/to/log.

How do I rotate cron job logs?

Use logrotate. Create a config in /etc/logrotate.d/<name> with the directives: daily, rotate 14, compress, missingok, notifempty. Test with logrotate -d.

How do I know if my cron job actually ran?

Add a heartbeat to the job — write a row to a database, increment a metric, or write to a file you can stat. Then alert on “no heartbeat in 25 hours” for a daily job.

Can a cron job run as a different user?

Yes, with the user-level crontab (crontab -u <user> -e) or with sudo -u <user> <command> in the system crontab. The latter requires the running user to have sudo permission.

Why does my cron job fail with “permission denied”?

Cron runs in a minimal environment without your shell rc files. The PATH is usually /usr/bin:/bin. If your command needs a custom PATH, set it in the crontab: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin. Same for JAVA_HOME, NODE_PATH, and any other env vars your script needs.

FAQ

Where is the cron daemon log?

/var/log/cron on RHEL-family, /var/log/syslog on Debian-family. On systemd: journalctl -u cron.

Why does my cron job have no output?

By default, cron emails the output to the crontab owner. With no MTA, the email is dropped. Fix: redirect to a log file with >> /path/to/log 2>&1.

How do I log to syslog?

Pipe to logger -t <tag> -p <facility>.<level>. The output goes through syslog.

How do I timestamp each line?

Wrap in a subshell that prefixes each line with $(date). Or use ts from moreutils.

How do I rotate cron job logs?

Use logrotate. Create a config in /etc/logrotate.d/<name> with daily, rotate 14, compress, missingok, notifempty.

How do I know if my cron job actually ran?

Add a heartbeat — write to a database, increment a metric, or write to a file. Alert on “no heartbeat in 25 hours” for a daily job.

Can a cron job run as a different user?

Yes, with crontab -u <user> -e or sudo -u <user> <command> in the system crontab.

Why does my cron job fail with “permission denied”?

Cron runs in a minimal environment without shell rc files. Set PATH and any required env vars in the crontab itself.

#Crontab#Cron#Linux#Logging#Operations