The script runs perfectly when you type its name. Scheduled in crontab, nothing happens: no backup file, no email, no error. A cron job not running is one of the most common small puzzles in Linux administration, and the cause is almost never cron itself. It is the difference between your interactive shell and the bare environment cron gives each job. Here are the usual causes, in the order worth checking, and how to build scheduled jobs you can actually trust.
1. Cron job not running? Start with the environment
Cron starts jobs with a minimal environment. It does not read .bashrc or .profile, and the default cron PATH is just /usr/bin:/bin. A command in /usr/local/bin or /usr/sbin, or a tool installed only for your user, is simply not found. Cron environment variables are also a short list: a database password exported in your profile, a proxy setting or a language setting you rely on interactively will not be there.
The fixes: use full paths for commands in the script, set PATH at the top of the crontab or the script, and load any configuration the job needs from a file the script reads explicitly. To see exactly what cron provides, schedule a one-off job that writes the output of env to a file in /tmp and compare it with your own shell.
2. The job runs under sh, not Bash
Unless the crontab sets SHELL, cron runs each command line with /bin/sh. On Debian and Ubuntu that is dash, a smaller shell that does not understand Bash features such as arrays, double-bracket tests or the source command. Start every script with a #!/bin/bash line, make it executable, and call the script from cron instead of pasting long Bash one-liners into the crontab.
3. Crontab not working because of the file itself
- The percent sign. In a crontab command line, cron turns an unescaped percent sign into a newline. A date format passed inline to the date command is the classic victim. Escape each one with a backslash, or move the command into a script.
- The last line. A crontab needs a newline after its final entry, or that entry may be ignored.
- The user field. Entries in /etc/crontab and /etc/cron.d include a user name between the schedule and the command; personal crontabs edited with crontab -e do not. Mixing up the two formats breaks the entry.
- File names in cron directories. On Debian-based systems, scripts in /etc/cron.daily and similar folders are started by run-parts, which skips file names that contain a dot. A script called backup.sh placed there never runs; name it backup instead. Files in /etc/cron.d follow the same naming rule.
- Permissions. Scripts in those folders must be executable, and a script owned by one user may not be readable by the account the job runs as.
4. It ran, just not when you expected
Cron follows the server's time zone. Many cloud servers run on UTC, so a job meant for 2 a.m. in Los Angeles actually fires in the early evening Pacific time, and the gap moves by an hour when daylight saving time starts or ends. Check the setting with timedatectl, then either write the schedule in the server's zone or change the zone deliberately. For jobs tied to business hours, note the time zone in a comment next to the entry.
5. It ran and failed silently: where to find cron logs
By default cron emails a job's output to its owner, which goes nowhere on servers without mail delivery. On current Debian and Ubuntu the cron service logs each job it starts to the systemd journal, so journalctl -u cron shows whether the job was launched at all. Debian 12 and later no longer install rsyslog by default, which is why /var/log/syslog may be missing when you go looking for cron logs. If the job was launched but produced nothing useful, redirect its output to a log file or set MAILTO to an address someone reads, and let the script report its own failures.
Making scheduled jobs trustworthy
Once the job runs, a few habits keep it running. They are what separate custom shell scripts you can forget about from ones that fail quietly for months, and they are the rules behind the Bash scripting and automation work we do:
- Strict mode. set -euo pipefail makes the script stop at the first failed command instead of carrying on with missing data.
- No overlapping runs. Wrapping the job in flock -n with a lock file means a slow run is skipped rather than doubled when the next one starts.
- A heartbeat. The job reports success to a monitoring check when it finishes; if the report does not arrive, someone gets an alert.
- Systemd timers for heavier jobs. Systemd timers log to the journal, can catch up on runs missed while the server was off with Persistent=true, and show the last and next run in systemctl list-timers.
Frequently asked questions: cron job not running
How do I test a cron job without waiting for the schedule?
Run it with the same bare environment: env -i with a minimal PATH, as the same user and through /bin/sh if that is what cron uses. If it works that way, it will work from cron. Scheduling it every minute for a short while and watching the journal also works.
Why does my cron job run twice?
Usually because it is scheduled in two places, for example in a personal crontab and in /etc/cron.d, or because the previous run was still going when the next one started. Check crontab -l for each user as well as /etc/crontab and /etc/cron.d, and add a lock with flock.
Why does my job work from crontab -e but not from /etc/cron.d?
Almost always the user field or the file name. Entries in /etc/cron.d need the user column, and a file name with a dot in it is ignored.
Tired of scripts that only work when someone is watching? Our custom Bash scripting and automation service writes, tests and documents scheduled jobs for backups, deployments and health checks, and our Bash scripting services also cover fixing scripts someone else wrote. For Linux automation services in Los Angeles and Long Beach, contact us with a short description of the job and the server it runs on.



