Opyntel

Cron job not running: find out why, and know the next time it fails

When a cron job is not running, first check whether cron started it at all: the cron log shows every job it launches. If cron ran it and nothing happened, the job failed in cron’s bare environment, usually because of a missing PATH, a relative path, a script that is not executable or a % sign cron treats as a newline. Redirect the job’s output to a file and the error is there. Cron only mails a job’s output, which on most servers goes nowhere, so a job that matters needs an alert when it fails or stops running.

Did cron start the job?

bash
systemctl status cron                    # Debian and Ubuntu; crond on Fedora and RHEL
grep CRON /var/log/syslog | tail -20     # Ubuntu, and Debian with rsyslog
grep CROND /var/log/cron | tail -20      # RHEL and its rebuilds
journalctl -u cron --since "1 hour ago"  # any systemd system (crond on RHEL and Fedora)
crontab -l                               # the jobs of the user you are logged in as

A CMD (...) line at the expected minute means cron ran it and the problem is the job. No line means cron never tried: the job is in another user’s crontab, the schedule is not what you think, or cron itself is not running.

The usual causes

CauseFix
PATH is only /usr/bin:/binUse full paths (/usr/local/bin/node), or set PATH= at the top of the crontab
Relative paths and the working directoryCron starts in the user’s home directory; cd /srv/app && first
Script not executable, or no shebangchmod +x and a #!/bin/bash first line, or call the interpreter
A % sign in the commandCron turns % into a newline; write \%, as in date +\%F
Wrong crontabsudo crontab -e is root’s; files in /etc/cron.d need a user field and a file name without dots
Environment variables missingCron does not read .bashrc or .profile; source an env file in the job
No newline at the end of the crontabCron ignores a last line without one
Server timezoneCron uses the server’s timezone; check it with timedatectl and restart cron after changing it

See the error

Cron mails a job’s output to the user, which on most servers goes nowhere. Send it to a file instead:

bash
*/5 * * * * /srv/app/sync.sh >> /var/log/sync.log 2>&1

To reproduce cron’s environment by hand, run the command with an almost empty environment: env -i HOME=$HOME PATH=/usr/bin:/bin /bin/sh -c '/srv/app/sync.sh'. If it fails there, it fails in cron.

In containers and on platforms

Inside a Docker container nothing starts the cron daemon unless the image does, so a crontab in a container usually never runs. Run the job from the host’s cron with docker exec, or use your platform’s scheduler: Coolify and Dokploy scheduled tasks, Render and Northflank cron jobs, DigitalOcean App Platform scheduled jobs, Cloudflare Cron Triggers or Vercel cron jobs.

A scheduled job that crashes on start in a container can also show up as a restart loop; see why a container keeps restarting.

How Opyntel tells you a scheduled job failed or never ran

Opyntel reads scheduled runs through the access you give it: the Opyntel agent on a server, a read-only token for platforms such as Render, Cloudflare and Coolify, or a log drain for Vercel on Pro and Enterprise. It alerts when a run fails and when an expected run does not happen at all, by email, Slack or SMS depending on your plan. Every job sits in one dashboard next to the services and servers it belongs to, so the backup that quietly stopped is found the day it stops.

FAQ

Cron job not running questions, answered

Logs, reboots, testing a schedule and why it works by hand.

Cron runs with a minimal PATH, no shell profile and the home directory as working directory. Use full paths and load the environment the job needs.

grep CRON /var/log/syslog on Ubuntu, /var/log/cron on RHEL, or journalctl -u cron (crond on RHEL and Fedora) on any systemd system, including Debian without rsyslog.

The cron service is not enabled at boot (systemctl enable cron), or the job depends on a mount or network that is not up yet. @reboot jobs run once, as cron starts.

Set the job to * * * * * for a minute, watch the cron log, then put the real schedule back.

The alerts your platform doesn’t send.

One dashboard for everything you run, with the alerts your platforms leave out. Read-only, nothing to install in your apps.

See how it works