Cron Job Not Running? A 12-Point Checklist That Finds It
Sagar Tayde · · 5 min read
A cron job not running is one of the most annoying problems in operations, because nothing tells you. There is no error on screen, and the job simply does not happen.
The cause is almost always one of a short list. Work through the checks below in order. The first few take seconds, and together they cover the vast majority of cases.
On this page
- First: did it run at all?
- Checks 1-4: is cron set up?
- Checks 5-8: does the command work under cron?
- Checks 9-12: is it running at the wrong time, or twice?
- Extra traps on hosted platforms
- A crontab line that avoids most of this
- FAQ
First: did it run at all?
Before changing anything, find out whether cron started the job and it failed, or never started it.
grep CRON /var/log/syslog # Debian and Ubuntu
journalctl -u cron --since today # systemd (the unit is crond on RHEL-family systems)
A line with your command means cron started it, so the problem is inside the command (checks 5 to 8). No line means cron never ran it (checks 1 to 4 and 9).

Checks 1-4: is cron set up?
1. Is the cron daemon running? On Ubuntu, systemctl status cron. In a minimal Docker container
there is usually no cron daemon at all unless you start one.
2. Is the job in the crontab you think? Run crontab -l as the same user. A job added with
sudo crontab -e lives in root’s crontab, not yours.
3. Is the syntax right? Standard cron takes five time fields. A sixth field for seconds, copied
from a library such as node-cron, makes the line invalid. Files in /etc/cron.d/ also need a user
name before the command.
4. Does the file end with a newline? Cron can ignore a last line with no newline after it. Press Enter after the final job.
Checks 5-8: does the command work under cron?
5. The PATH is short. Cron runs with a minimal PATH, often just /usr/bin:/bin. A command such as
node, python3 or php installed elsewhere is not found. Use full paths (which node shows it), or
set PATH= at the top of the crontab.
6. Your environment is not loaded. Cron does not read .bashrc or .profile. Variables such as
DATABASE_URL that you export in your shell are missing. Load them explicitly in the script.
7. The % sign is special. In a crontab, % means “new line”. A command such as
date +%Y-%m-%d breaks unless you write \%. Moving the command into a script avoids it.
8. Permissions and the working directory. The script must be executable (chmod +x), and cron
starts in your home directory. A script that opens config.json by relative path fails. cd to the
right folder first.
Checks 9-12: is it running at the wrong time, or twice?
9. The timezone. Cron uses the server’s timezone, usually UTC in the cloud. A job meant for 9 am in
India written as 0 9 * * * runs at 2:30 pm. Write 30 3 * * *, or change the server timezone.
10. Day-of-month and day-of-week. If both are set, standard cron runs when either matches.
0 9 1 * 1 runs on the 1st and every Monday, which looks like “running at random”.
11. Overlapping runs. A slow run is still going when the next one starts. They clash, and one
seems to fail. Wrap the command in flock -n /tmp/job.lock so a second copy is skipped.
12. It runs on two machines. The same crontab on two servers, or an in-app scheduler in two instances, runs every job twice. We measured exactly that with node-cron and APScheduler.
Extra traps on hosted platforms
| Platform | What stops a job running |
|---|---|
| GitHub Actions | Minimum 5-minute interval; delays at busy times; disabled after 60 days without activity in public repositories |
| Vercel | Only production deployments run crons; redirects (such as trailingSlash) end the run; Hobby allows one run a day on legacy Cron Jobs |
| Heroku Scheduler | A run is stopped at its interval; runs are best-effort |
| Celery beat | Two beat processes double tasks; a lost schedule file loses last-run times |
| Voroa | The parent service must be running; runs are skipped while it is stopped, including after a free workspace uses its monthly free hours |
Sources: the GitHub Actions schedule docs and each platform’s own documentation. More detail per platform is in our posts on Vercel cron jobs and Heroku Scheduler.
A crontab line that avoids most of this
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
CRON_TZ=Asia/Kolkata
30 9 * * * cd /srv/app && flock -n /tmp/digest.lock ./bin/digest >> /var/log/digest.log 2>&1
It sets a shell and a PATH, runs from the right folder, takes a lock, and logs both output and errors.
CRON_TZ is honoured by cronie (Fedora, RHEL); Ubuntu’s default cron ignores it, so check yours.
On Voroa
A Voroa Cron Job or Scheduled Task runs your command inside a service that stays running, with your environment variables already set and schedules in India Standard Time. That removes checks 5, 6 and 9, though the container’s own clock may still read UTC if your job reads the date.
Voroa does not show per-run output for a scheduled command, so make the job record its result somewhere you can check, such as a database row. See scheduling a task on Voroa and worker or cron job. Long-running processes are covered on the background workers page.
FAQ
Why is my cron job not running? Most often: a missing PATH, a missing environment variable, a timezone difference, or the job is in a different user’s crontab. Check the cron log first to see whether it started at all.
How do I check if cron ran my job?
Look for your command in /var/log/syslog (Debian, Ubuntu) or journalctl -u cron, and send the job’s
own output to a log file.
Why does my script work in the terminal but not in cron? Cron has a short PATH, does not load your shell profile, and starts in your home directory. Use full paths and load your variables in the script.
Why does my cron job run at the wrong time?
The server is probably on UTC. For 9 am IST on a UTC server, write 30 3 * * *.
How do I stop a cron job running twice?
Use flock -n against overlap, and make sure only one machine or one scheduler instance runs the job.
Does cron run if the server was off?
No. Standard cron does not catch up missed runs. systemd timers can, with Persistent=true.
Updated 7 November 2026. Found something out of date? Write to support@getvoroa.com.