632

Often, crontab scripts are not executed on schedule or as expected. There are numerous reasons for that:

  1. wrong crontab notation
  2. permissions problem
  3. environment variables

This community wiki aims to aggregate the top reasons for crontab scripts not being executed as expected. Write each reason in a separate answer.

Please include one reason per answer - details about why it's not executed - and fix(es) for that one reason.

Please write only cron-specific issues, e.g. commands that execute as expected from the shell but execute erroneously by cron.

Adam Matan
  • 12,519
  • 18
    You must close crontab -e for the cron to take affect. For instance using vim I edit the file and use :w to write it but the job is not added to cron until I quit also. So I will not see the job until after I :q also. – DutGRIFF Jun 24 '14 at 14:58
  • I think best way to debug cron is to check syslog and find the problems. – Suneel Kumar Jun 23 '16 at 07:49
  • Electricity outages – Josef Klimuk Mar 21 '18 at 07:38
  • Please check this one https://askubuntu.com/a/1223213/297387 – Vadim Apr 01 '20 at 16:15
  • Just one issue, which causes me some time to find out: I had '0 * * * * /pathtoscript/script'. What I have overseen is that the script was not working in teh expected folder. A simple 'cd /pathtoscript' solved my issues. ... took me some hours – BerndGit Feb 20 '22 at 15:03

47 Answers47

606

Different environment

Cron passes a minimal set of environment variables to your jobs. To see the difference, add a dummy job like this:

* * * * * env > /tmp/env.output

Wait for /tmp/env.output to be created, then remove the job again. Now compare the contents of /tmp/env.output with the output of env run in your regular terminal.

A common "gotcha" here is the PATH environment variable being different. Maybe your cron script uses the command somecommand found in /opt/someApp/bin, which you've added to PATH in /etc/environment? cron ignores PATH from that file, so runnning somecommand from your script will fail when run with cron, but work when run in a terminal. It's worth noting that variables from /etc/environment will be passed on to cron jobs, just not the variables cron specifically sets itself, such as PATH.

To get around that, just set your own PATH variable at the top of the script. E.g.

#!/bin/bash
PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# rest of script follows

Some prefer to just use absolute paths to all the commands instead. I recommend against that. Consider what happens if you want to run your script on a different system, and on that system, the command is in /opt/someAppv2.2/bin instead. You'd have to go through the whole script replacing /opt/someApp/bin with /opt/someAppv2.2/bin instead of just doing a small edit on the first line of the script.

You can also set the PATH variable in the crontab file, which will apply to all cron jobs. E.g.

PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

15 1 * * * backupscript --incremental /home /root
dessert
  • 39,982
geirha
  • 46,101
  • 7
    I think I just fell for this, and newline at end... double whammy. – WernerCD Jun 02 '11 at 04:22
  • +1 That was the primary reason why I had a headache with a cronned script that used grep and ls, the path was empty under cron's env, heh. – DM8 Jun 25 '11 at 20:09
  • 9
    +1 for env, I had completely forgotten about that command and thought PATH was working. It was actually sliiiightly different in my case. – Izkata Jan 18 '12 at 15:16
  • Counting on the PATH is a bad idea. "because an exe may be elsewhere on some other computer" does NOT trump "I want my cron job to run exactly the exe I specify, and not some other exe someone may someday put in a directory listed earlier in the PATH than the one my original exe is in" - - think about it - someone could put a new (tainted) version of "bash" or "mail" or any other program you're calling without a full path into /opt/someApp/bin ...and magically, they OWN your system when some ROOT cronjob runs their tainted exe instead of the one it should be running. – pbr Apr 08 '12 at 23:03
  • 9
    @pbr If such directories are left writable to others, the system is already compromized. – geirha Apr 09 '12 at 08:23
  • @geirha another sysadmin could unwittingly place new exes in the directories - possibly newer versions of language interpreters for example, which might not work with the older code being called by cron. ALSO - in your example a non-standard dir is added to the FRONT of the path - /opt/someApp/bin - goodness knows who the owner/group are on that, and what the perms are. Easy enough for there to be an unintentional "hole" an attacker could leverage. I stick to my original statement - counting on the PATH is a bad idea, use fully qualified paths to the executables. – pbr Apr 09 '12 at 18:08
  • 6
    @pbr A sysadmin could unwittingly delete the root filesystem. You can't guard against sysadmins making silly mistakes. If you install a newer version of an interpreter that is not backwards compatible, I'd expect breakage regardless. The sane way to handle that is to install it as a different command. E.g. you have python version 2.x and install python 3, you install it as python3, not python. And as for /opt/someApp/bin, why on earth wouldn't it have sane permissions/ownership? any sane admin would ensure sane permissions/ownership on system files. – geirha Apr 10 '12 at 06:36
  • @geirha we could go back and forth all year on this, but I won't - this will be my final update on this matter. I agree you can't guard against sysadmins making silly mistakes. Like, setting a PATH instead of using full pathnames in their cron specifications. /opt/someApp/bin is neither a "system file" nor a "system directory" - it's a 3rd party app directory being put in the FRONT of a PATH for crontab in your example. If that's ROOTs crontab, that's a REALLY BAD IDEA. Period. It's really quite simple - the best practice is using complete pathnames, not counting on a PATH setting. – pbr Apr 11 '12 at 01:13
  • 2
    @pbr It seems we could go on forever, yes. I still fail to see why it's a bad idea to use PATH though. If you feel like discussing this further in a medium better suited for discussion, you'll find me in #ubuntu and #bash, among other channels, on irc.freenode.net – geirha Apr 11 '12 at 16:28
  • took longer to get the cron running than to write the script. It was indeed a path issue as you suggest above. thanks! – eggie5 Jul 31 '12 at 22:32
  • I am getting this error "sh: 0: Can't open /root/scritps/cron_job.sh", when trying to execute sh file from cronjob like "/1 * * * sh /root/scritps/cron_job.sh >> /tmp/cron.output 2>&1".FYI: cron_job.sh doing echo only. any idea what is the issue here ? – Bipin Vayalu Jun 23 '14 at 16:26
  • @BipinVayalu, that probably means it either doesn't have access to that file, or the path does not exist. Perhaps it's scripts rather than scritps? – geirha Jun 25 '14 at 13:27
  • Do I get it right that adding /opt/someApp/bin is better done to the end of PATH rather than to the front? Edit done in advance. – ulidtko Oct 14 '14 at 13:38
  • @ulidtko, I'd say no, you'd typically want the executables in /opt/someApp/bin to take precedence over commands that happen to have the same name in other directories. The order was intentional. – geirha Oct 15 '14 at 12:48
  • I'm inclined to think otherwise. These /opt-style packages are not typically designed to shadow system commands, usually they provide their own unique command names. Which means they'd be fine in the end of PATH. OTOH, as @pbr somewhat reasonably argues, shadowing system commands by design is very possibly a malware signature. Personally I'd prefer to stay on the safe side by default (i.e. end of PATH), and only move to the front after complete inspection of package/directory contents and appropriate motivation for shadowing. – ulidtko Oct 15 '14 at 13:00
  • Which summarizes to the conclusion: for users who don't care, advise end of PATH. Only when it doesn't work that way, try front -- but first take some care of your system security. – ulidtko Oct 15 '14 at 13:05
  • @ulidtko ... or when the cronjob suddenly stopped working because you installed package foo, which happened to install a command in /usr/bin with the same name as one you use from someApp, but doing something completely different, of course. That could have disastrous effects. You obviously have to trust that the commands in /opt/someApp/bin are not malicious. If there are malicious commands in there, the system administrators have done a poor job. – geirha Oct 15 '14 at 14:42
  • Hm, that's a valid objection too. – ulidtko Oct 15 '14 at 14:47
  • I have sometimes put something like this * * * * * source ~/.bashrc; command to get the environment setup as in my normal login session – mcfedr Nov 19 '15 at 19:28
  • In particular, graphics (X11) commands won't work; see http://askubuntu.com/a/92195/56280. – ntc2 Mar 01 '16 at 23:08
  • 1
    Thanks for this, brilliant. Solved an issue of mine as I had never thought of the missing paths. – Hugh Aug 18 '17 at 19:51
  • You can also pass the variable to your script through cron like so: "/5 * * * env HOME=/root /path/to/script" – Tony Barganski Oct 15 '18 at 09:48
  • If you modified timezone on a system, one must either restart every service that cares about what time it is, or reboot, This comment I copied from an answer below. – Arun Nov 07 '21 at 23:45
  • With PATH=/usr/sbin It does not work. I had to do export PATH=/usr/sbin – PouJa May 14 '23 at 17:37
420

My top gotcha: If you forget to add a newline at the end of the crontab file. In other words, the crontab file should end with an empty line.

Below is the relevant section in the man pages for this issue (man crontab then skip to the end):

   Although cron requires that each entry in a crontab end  in  a  newline
   character,  neither the crontab command nor the cron daemon will detect
   this error. Instead, the crontab will appear to load normally. However,
   the  command  will  never  run.  The best choice is to ensure that your
   crontab has a blank line at the end.

   4th Berkeley Distribution      29 December 1993               CRONTAB(1)
8128
  • 28,740
user4124
  • 8,911
  • 126
    this is such a showstopper, how come it hasn't been fixed in so many years of cron? – Capi Etheriel Jan 27 '11 at 03:20
  • 2
    Seems to be fixed in Vixie cron: man crontab on Ubuntu 10.10 says "cron requires that each entry in a crontab end in a newline character. If the last entry in a crontab is missing the newline, cron will consider the crontab (at least partially) broken and refuse to install it." (And the date at the end is 19 April 2010.) – Marius Gedminas Feb 01 '11 at 22:58
  • 21
    @barraponto This is actually a bug in new text editors. The "newline" character is supposed to be a line termination character, so the final line in a text file is supposed to end in a newline character that doesn't get shown in the editor. Vi and vim use the character correctly, and cron was built before the new editors started their odd behavior... Hence playing it save and including a blank line. – Izkata Jan 18 '12 at 15:20
  • 1
    crontabs were broken (in my mind) until now… quite a revelation… but same thoughts as @barraponto – Ben May 08 '14 at 14:56
  • 9
    If you edit crontab using crontab -e it will check the syntax of the file before allowing a save, including a check for newline. – Tom Harrison Jr Sep 26 '14 at 18:26
  • 1
    @TomHarrisonJr That's misleading. 'crontab -e' uses the editor you set in your environment. If it's vi, then it will append newline to the last line. But crontab doesn't check lines end with newline, as clearly stated in the man page excerpt in the answer. – Chan-Ho Suh Nov 14 '14 at 04:48
  • 2
    @Chan-HoSuh, according to man page "cron requires that each entry in a crontab end in a newline character. If the last entry in a crontab is missing the newline, cron will consider the crontab (at least partially) broken and refuse to install it." This behavior will be invoked when editing then saving the crontab using the -e option, and is independent of the editor. – Tom Harrison Jr Nov 18 '14 at 16:40
  • Thank you for your answer. I have had many times that crontab -e did not tell me that there is no newline. Simply pressing enter at the end of the line, the saving it worked great! – Terrance Jun 04 '15 at 20:25
  • Thanks for the tip. I have this issue with Ubuntu 12.04.1 LTS. – Mohamed Ennahdi El Idrissi Jun 15 '15 at 16:54
  • This is strange, I have worked on multiple servers one of them had the problem and I fixed by adding the new line at the end as suggested and it worked. The strange thing is the other server has a crontab that is running normally with no problem with no newline included in the last entry, to make it even weirder all servers are running on the same OS. – Mohyaddin Alaoddin May 29 '17 at 07:51
  • Thank you for this fix. It's like the open source folks are trying to make everyone's life harder on purpose. A missing line break that makes cron silently stop working without any errors or warnings? Again, thanks for guiding us through this insanity. – nicbou Dec 25 '17 at 18:15
  • I just noticed a similar issue: if you call a shell script that doesn't and in a newline. – user114676 Jul 20 '18 at 11:38
  • WHAT THE .... BUT WHY? – Nabin May 05 '20 at 13:02
  • I realize this may be very late advice. I ran into this issue. It is important that there are no comment lines at the end of the file. In some special case, it stops working. I just add the actual cron commands to the end of the file without comments. An empty line at the end is also needed. You also need to validate the user if you are using one. Does cron have access to this environment? – oshliaer Apr 22 '22 at 05:36
184

Cron daemon is not running. I really screwed up with this some months ago.

Type:

pgrep cron 

If you see no number (i.e. cron's main PID), then cron is not running. sudo /etc/init.d/cron start can be used to start cron.

EDIT: Rather than invoking init scripts through /etc/init.d, use the service utility, e.g.

sudo service cron start

EDIT: Also you could use systemctl in modern Linux, e.g.

sudo systemctl start cron
manasouza
  • 101
  • 4
aneeshep
  • 30,321
112

The script filenames in cron.d/, cron.daily/, cron.hourly/, etc., should NOT contain dot (.), otherwise run-parts will skip them.

See run-parts(8):

   If neither the --lsbsysinit option nor the --regex option is given then
   the names must consist entirely of upper and lower case  letters,  dig‐
   its, underscores, and hyphens.

If the --lsbsysinit option is given, then the names must not end in .dpkg-old or .dpkg-dist or .dpkg-new or .dpkg-tmp, and must belong to one or more of the following namespaces: the LANANA-assigned namespace (^[a-z0-9]+$); the LSB hierarchical and reserved namespaces (^?([a-z0-9.]+-)+[a-z0-9]+$); and the Debian cron script namespace (^[a-zA-Z0-9_-]+$).

So, if you have a cron script backup.sh, analyze-logs.pl in cron.daily/ directory, you'd best to remove the extension names.

yurenchen
  • 441
Lenik
  • 10,398
  • 12
    It's a feature not a bug - it keeps things like myscript.backup or myscript.original or myscript.rpm-new from running right beside myscript. – pbr Apr 08 '12 at 22:45
  • @pbr: makes sense. At least it would have been helpful for debugging if run-parts --test (or another imaginary option like --debug would output the files it skips including the reason. – Rabarberski May 30 '13 at 08:46
  • 14
    If this is a feature, it's not a nice one :( A lot of people use dot in file name (backup.sh is the most common one). If you want to a script to stop executing, the most logical method will be to remove it from "cron.d" directory. – MatuDuke May 16 '14 at 13:59
  • 12
    This is such a bad feature that it's effectively a bug. It's common practice to require a particular ending (like ".list" or ".cron" or something) if people want to make sure that things only get run when intended. Arbitrarily picking dot as a likely separator for ".bak" or ".temp" or whatever, is completely unpredictable except in the way that it will predictably confuse people. Legitimate endings like ".sh", and ".pl" have been in widespread use for decades. Lots of people use "_bak" or "_temp" or "-bak" instead of a dot, however. This is an awful design choice; it's a design bug at best. – Teekin May 10 '17 at 20:42
  • Wait, does that mean that my shell script pull-data.sh will never execute even if I add this command to the crontab: /bin/bash /home/userName/pull-data.sh ? – RodrikTheReader Sep 25 '20 at 01:21
83

In many environments cron executes commands using sh, while many people assume it will use bash.

Suggestions to test or fix this for a failing command:

  • Try running the command in sh to see if it works:

    sh -c "mycommand"
    
  • Wrap the command in a bash subshell to make sure it gets run in bash:

    bash -c "mybashcommand"
    
  • Tell cron to run all commands in bash by setting the shell at the top of your crontab:

    SHELL=/bin/bash
    
  • If the command is a script, make sure the script contains a shebang:

    #!/bin/bash
    
Olorin
  • 3,488
  • 1
    bash suggestion is very helpful, fixed issue with my cron. – Maxim Galushka Feb 18 '16 at 22:38
  • 1
    This just caused me 1hr of fiddling/troubleshooting. Even more perplexing if you're not aware of the issue is the script will run manually just fine if you're typical shell is bash, but not with cron. Thanks! – Hendy Nov 22 '16 at 19:42
  • 1
    A long time ago I ran into something related: The command source is in bash but not sh. In cron/sh, use a period: . envfile rather than source envfile. – kungphu Dec 22 '17 at 02:24
  • @Clockwork sh "mycommand" tells sh to run mycommand as a script file. Did you mean sh -c "mycommand"? At any rate, this answer seems to be about making the command run in bash specifically, so why did you add the command for sh here? – Olorin Feb 08 '19 at 09:53
  • 1
    @Olorin From my understanding, the objective of the first point was to try and run it with sh, to see if the problem truly came from the fact that cron is running it with sh instead of bash. Then again, I have little knowledge about the matter, so I might be wrong. – Clockwork Feb 08 '19 at 10:12
54

I had some issues with the time zones. Cron was running with the fresh installation time zone. The solution was to restart cron:

sudo service cron restart
  • 8
    Yes, after changing the timezone on a system, one must either restart every service that cares about what time it is, or reboot. I prefer the reboot, to be sure I've caught everything. – pbr Apr 08 '12 at 22:48
  • Oh for God's sake, killed hours on this. Tried service restart after * * * * * touch /tmp/cronworks did nothing, yet there is RELOAD at cronlog. – Evgeniy Chekan Oct 01 '14 at 03:57
  • this solved it, even though there was no problem with my timezone – Aven Desta May 16 '20 at 16:08
  • Wow!! this saved my life – Arun Nov 07 '21 at 23:41
  • You lose hours, when you think finding the root cause and fixing it is faster than a quick reboot's magic. – Paiman Roointan Oct 14 '23 at 05:08
44

Absolute path should be used for scripts:

For example, /bin/grep should be used instead of grep:

# m h  dom mon dow   command
0 0 *  *  *  /bin/grep ERROR /home/adam/run.log &> /tmp/errors

Instead of:

# m h  dom mon dow   command
0 0 *  *  *  grep ERROR /home/adam/run.log &> /tmp/errors

This is especially tricky, because the same command will work when executed from shell. The reason is that cron does not have the same PATH environment variable as the user.

Adam Matan
  • 12,519
  • 3
    see geirha answer, you can (must) define cron's PATH – Capi Etheriel Jan 27 '11 at 03:22
  • 11
    Bzzt. you do NOT need to define the PATH - using absolute paths is the best practice here. "because an executable may be elsewhere on some other computer" doesn't trump "I want it to run exactly this program and not some other one someone put in the path in front of my original program" – pbr Apr 08 '12 at 22:55
  • 1
    yep this was it for me, outside the cron I could run the command directly, inside the cron it needed full /usr/bin/whatever path – Anentropic Aug 14 '16 at 10:56
38

If your crontab command has a % symbol in it, cron tries to interpret it. So if you were using any command with a % in it (such as a format specification to the date command) you will need to escape it.

That and other good gotchas here:
http://www.pantz.org/software/cron/croninfo.html

Eliah Kagan
  • 117,780
JMS
  • 1
29

Cron is calling a script that is not executable.

By running chmod +x /path/to/script, the script becomes executable and this should resolve this issue.

jet
  • 7,274
  • 6
    That's not unique to cron, and easily traceable by simply trying to execute /path/to/script from the command line. – Adam Matan Jan 27 '11 at 06:33
  • 4
    If you're used to executing scripts with . scriptname or sh scriptname or bash scriptname, then this becomes a cron-specific problem. – Eliah Kagan Nov 24 '11 at 23:09
27

It is also possible that the user's password has expired. Even root's password can expire. You can tail -f /var/log/cron.log and you will see cron fail with password expired. You can set the password to never expire by doing this: passwd -x -1 <username>

In some systems (Debian, Ubuntu) logging for cron is not enabled by default. In /etc/rsyslog.conf or /etc/rsyslog.d/50-default.conf the line:

# cron.*                          /var/log/cron.log

should be edited (sudo nano /etc/rsyslog.conf) uncommented to:

cron.*                          /var/log/cron.log

After that, you need to restart rsyslog via

/etc/init.d/rsyslog restart

or

service rsyslog restart 

Source: Enable crontab logging in Debian Linux

In some systems (Ubuntu) separate logging file for cron is not enabled by default, but cron related logs are appearing in syslog file. One may use

cat /var/log/syslog | grep cron -i

to view cron-related messages.

jdow
  • 101
  • I have Debian (wheezy) but there is no /etc/init.d/rsyslog, only inetutils-syslogd and sysklogd. Do I have to install something or just restart one of the two? – hgoebl Oct 21 '16 at 11:41
21

If your cronjob invokes GUI-apps, you need to tell them what DISPLAY they should use.

Example: Firefox launch with cron.

Your script should contain export DISPLAY=:0 somewhere.

jokerdino
  • 41,320
andrew
  • 1
18

Insecure cron table permission

A cron table is rejected if its permission is insecure

sudo service cron restart
grep -i cron /var/log/syslog|tail -2
2013-02-05T03:47:49.283841+01:00 ubuntu cron[49906]: (user) INSECURE MODE (mode 0600 expected) (crontabs/user)

The problem is solved with

# correct permission
sudo chmod 600 /var/spool/cron/crontabs/user
# signal crond to reload the file
sudo touch /var/spool/cron/crontabs
  • First I figured it out myself and then I found your answer! Still thanks a lot! In my case, I had reverted/restored some crontabs in /var/spool/cron/crontabs via SVN which changed its permissions! – alfonx Jun 08 '13 at 20:40
  • grep -i cron /var/log/syslog|tail -2 this helps me to show what errors blocked the cron script – Ankit24007 Jul 28 '21 at 03:40
15

Permissions problems are quite common, I'm afraid.

Note that a common workaround is to execute everything using root's crontab, which sometimes is a Really Bad Idea. Setting proper permissions is definitely a largely overlooked issue.

  • 1
    Note that if you have a crontab line that is set to pipe output to a file that does not yet exist, and the directory for the file is one that the cron user doesn't have access to, then the line will not execute. – Evan Donovan Sep 10 '15 at 16:51
13

Script is location-sensitive. This is related to always using absolute paths in a script, but not quite the same. Your cron job may need to cd to a specific directory before running, e.g. a rake task on a Rails application may need to be in the application root for Rake to find the correct task, not to mention the appropriate database configuration, etc.

So a crontab entry of

23 3 * * * /usr/bin/rake db:session_purge RAILS_ENV=production

would be better as

23 3 * * * cd /var/www/production/current && /usr/bin/rake db:session_purge RAILS_ENV=production

Or, to keep the crontab entry simpler and less brittle:

23 3 * * * /home/<user>/scripts/session-purge.sh

with the following code in /home/<user>/scripts/session-purge.sh:

cd /var/www/production/current
/usr/bin/rake db:session_purge RAILS_ENV=production
pjmorse
  • 101
  • 2
    If the script being invoked from cron is written in an interpreted language like PHP, you may need to set the working directory in the script itself. For example, in PHP:

    chdir(dirname(__FILE__));

    – Evan Donovan Sep 10 '15 at 16:14
  • 1
    Just got caught with this one: the script used to be in the root of my home directory, but then I moved it (and updated the crontab) and couldn't figure out why it wasn't working. Turns out the script was using a relative path, assuming that it was relative to the location of the script but it was in fact relative to the root of my home directory because that was the working directory that cron was using, which is why the script worked when it was in the root of my home directory (because the script's expected working directory and it's actual working just happened to coincide). – micheal65536 Feb 04 '16 at 18:36
12

Crontab specs which worked in the past can break when moved from one crontab file to another. Sometimes the reason is that you've moved the spec from a system crontab file to a user crontab file or vice-versa.

The cron job specification format differs between users' crontab files (/var/spool/cron/username or /var/spool/cron/crontabs/username) and the system crontabs (/etc/crontab and the the files in /etc/cron.d).

The system crontabs have an extra field 'user' right before the command-to-run.

This will cause errors stating things like george; command not found when you move a command out of /etc/crontab or a file in /etc/cron.d into a user's crontab file.

Conversely, cron will deliver errors like /usr/bin/restartxyz is not a valid username or similar when the reverse occurs.

Urhixidur
  • 233
pbr
  • 248
12

The most frequent reason I have seen cron fail in an incorrectly stated schedule. It takes practice to specify a job scheduled for 11:15 pm as 15 23 * * * instead of * * 11 15 * or 11 15 * * *. Day of the week for jobs after midnight also gets confused M-F is 2-6 after midnight, not 1-5. Specific dates are usually a problem as we rarely use them * * 3 1 * is not March 1st. If you are not sure, check your cron schedules online at https://crontab.guru/.

If your work with different platforms using unsupported options such as 2/3 in time specifications can also cause failures. This is a very useful option but not universally available. I have also run across issues with lists like 1-5 or 1,3,5.

Using unqualified paths have also caused problems. The default path is usually /bin:/usr/bin so only standard commands will run. These directories usually don't have the desired command. This also affects scripts using non-standard commands. Other environment variables can also be missing.

Clobbering an existing crontab entirely has caused me problems. I now load from a file copy. This can be recovered from the existing crontab using crontab -l if it gets clobbered. I keep the copy of crontab in ~/bin. It is commented throughout and ends with the line # EOF. This is reloaded daily from a crontab entry like:

#!/usr/bin/crontab
# Reload this crontab
#
54 12    *   *   *   ${HOME}/bin/crontab

The reload command above relies on an executable crontab with a bang path running crontab. Some systems require the running crontab in the command and specifying the file. If the directory is network-shared, then I often use crontab.$(hostname) as the name of the file. This will eventually correct cases where the wrong crontab is loaded on the wrong server.

Using the file provides a backup of what the crontab should be, and allows temporary edits (the only time I use crontab -e) to be backed out automatically. There are headers available which help with getting the scheduling parameters right. I have added them when inexperienced users would be editing a crontab.

Rarely, I have run into commands that require user input. These fail under crontab, although some will work with input redirection.

BillThor
  • 4,698
  • 3
    This covers three separate problems. Can they be split into separate answers? – Eliah Kagan Nov 24 '11 at 23:07
  • 9
    Can you explain how 30 23 * * * translates to 11:15 PM? – JYelton Jan 10 '14 at 19:23
  • @JYelton That was obviously wrong, it should be 15 23 * * *. Corrected now. – Melebius Jun 19 '19 at 08:09
  • timezones should be added here: my server is on UTC which is quite different from my actual timezone; this has no bearing on minutes, but the moment you get into the hours.... I just spent a good while wondering why a cronjob that I was trying to test every minute in my current hour was not working... – Fons MA Aug 20 '20 at 01:34
11

If you have a command like this:

* * * * * /path/to/script >> /tmp/output

and it doesn't work and you can't see any output, it may not necessarily mean cron isn't working. The script could be broken and the output going to stderr which doesn't get passed to /tmp/output. Check this isn't the case, by capturing this output as well:

* * * * * /path/to/script >> /tmp/output 2>&1

to see if this helps you catch your issue.

10

cron script is invoking a command with --verbose option

I had a cron script fail on me because I was in autopilot while typing the script and I included the --verbose option:

#!/bin/bash
some commands
tar cvfz /my/archive/file.tar.gz /my/shared/directory
come more commands

The script ran fine when executing from shell, but failed when running from crontab because the verbose output goes to stdout when run from shell, but nowhere when run from crontab. Easy fix to remove the 'v':

#!/bin/bash
some commands
tar cfz /my/archive/file.tar.gz /my/shared/directory
some more commands
Eliah Kagan
  • 117,780
  • 5
    Why is this causing a failure? Buffer issues? – Adam Matan May 31 '12 at 06:38
  • Any outputs or errors trriggred via cron jobs is gooing to sent to your mailbox.So we should never forget to care about these errors/output.We can redirect them to any file or /dev/null – Nischay Sep 27 '13 at 11:32
7

If you are accessing an account via SSH keys it is possible to login to the account but not notice that the password on the account is locked (e.g. due to expiring or invalid password attempts)

If the system is using PAM and the account is locked, this can stop its cronjob from running. (I've tested this on Solaris, but not on Ubuntu)

You may find messages like this in /var/adm/messages:

Oct 24 07:51:00 mybox cron[29024]: [ID 731128 auth.notice] pam_unix_account: cron attempting to validate locked account myuser from local host
Oct 24 07:52:00 mybox cron[29063]: [ID 731128 auth.notice] pam_unix_account: cron attempting to validate locked account myuser from local host
Oct 24 07:53:00 mybox cron[29098]: [ID 731128 auth.notice] pam_unix_account: cron attempting to validate locked account myuser from local host
Oct 24 07:54:00 mybox cron[29527]: [ID 731128 auth.notice] pam_unix_account: cron attempting to validate locked account myuser from local host

All you should need to do is run:

# passwd -u <USERNAME>

as root to unlock the account, and the crontab should work again.

JohnGH
  • 158
3

I was writing an install shell script that creates another script to purge old transaction data from a database. As a part of the task it had to configure daily cron job to run at an arbitrary time, when the database load was low.

I created a file mycronjob with cron schedule, username & the command and copied it to the /etc/cron.d directory. My two gotchas:

  1. mycronjob file had to be owned by root to run
  2. I had to set permissions of the file to 644 - 664 would not run.

A permission problem will appear in /var/log/syslog as something similar to:

Apr 24 18:30:01 ip-11-22-33-44 cron[40980]: (*system*) INSECURE MODE (group/other writable) (/etc/crontab)
Apr 24 18:30:01 ip-11-22-33-44 cron[40980]: (*system*) INSECURE MODE (group/other writable) (/etc/cron.d/user)

The first line refers to /etc/crontab file and the later to a file I placed under /etc/cront.d.

Volker Siegel
  • 13,065
  • 5
  • 49
  • 65
3

=== Docker alert ===

If you're using docker,

I think it is proper to add that I couldn't manage to make cron to run in the background.

To run a cron job inside the container, I used supervisor and ran cron -f, together with the other process.

Edit: Another issue - I also didn't manage to get it work when running the container with HOST networking. See this issue here also: https://github.com/phusion/baseimage-docker/issues/144

AlonL
  • 123
2

The default .bashrc files on UBuntu 16.04 (and many other versions) have a built in mechanism to not do anything if they are not an interactive shell.

This can prevent the file from being sourced correctly inside a script that is run by cron! To prevent this, comment out the following lines from the top of your .bashrc file:

Ubuntu 16.04

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

Ubuntu 12.04 and some other versions

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

So long as these are left in if cron runs a script which sources your .bashrc file you won't actually get any of the environment variables you are used to inside your terminal!

GrayedFox
  • 191
2

Writing to cron via "crontab -e" with the username argument in a line. I've seen examples of users (or sysadmins) writing their shell scripts and not understanding why they don't automate. The "user" argument exists in /etc/crontab, but not the user-defined files. so, for example, your personal file would be something like:

# m h dom mon dow command

* * */2  *   *  /some/shell/script

whereas /etc/crontab would be:

# m h dom mon dow user   command

* * */2  *   *  jdoe   /some/shell/script

So, why would you do the latter? Well, depending on how you want to set your permissions, this can become very convoluted. I've written scripts to automate tasks for users who don't understand the intricacies, or don't want to bother with the drudgery. By setting permissions to --x------, I can make the script executable without them being able to read (and perhaps accidentally change) it. However, I might want to run this command with several others from one file (thus making it easier to maintain) but make sure file output is assigned the right owner. Doing so (at least in Ubuntu 10.10) breaks on both the inability to read the file as well as execute, plus the afore-mentioned issue with putting periods in /etc/crontab (which, funnily enough, causes no error when going through crontab -e).

As an example, I've seen instances of sudo crontab -e used to run a script with root permissions, with a corresponding chown username file_output in the shell script. Sloppy, but it works. IMHO, The more graceful option is to put it in /etc/crontab with username declared and proper permissions, so file_output goes to the right place and owner.

Eliah Kagan
  • 117,780
Mange
  • 1
  • "Doing so (at least in Ubuntu 10.10) breaks on both the inability to read the file as well as execute....." I should clarify: /etc/crontab (by default) requires read AND execute permissions, whereas you COULD run "sudo crontab -e" to create a cronjob which overrides both the need for the "w" permission, and the problem with extensions like ".sh". I haven't had time to pull apart the cron code and check why this works, just a detail I've noticed. – Mange Jun 12 '12 at 19:54
2

Building off what Aaron Peart mentioned about verbose mode, sometimes scripts not in verbose mode will initialize but not finish if the default behavior of an included command is to output a line or more to the screen once the proc starts. For example, I wrote a backup script for our intranet which used curl, a utility that downloads or uploads files to remote servers, and is quite handy if you can only access said remote files through HTTP. Using 'curl http://something.com/somefile.xls' was causing a script I wrote to hang and never complete because it spits out a newline followed by a progress line. I had to use the silent flag (-s) to tell it not to output any information, and write in my own code to handle if the file failed to download.

Mange
  • 1
  • 1
    For programs that don't have a silent mode, you can redirect their output to /dev/null. For example: some-command > /dev/null This will redirect only standard output, and not error output (which is usually what you want, since you want to be informed of errors). To redirect error output too, use some-command &> /dev/null. – Eliah Kagan Jun 12 '12 at 20:22
  • Yeah, that was my first thought when writing the afore-mentioneed script. I forget why I didn't use that, possibly some non-standard behavior that circumvented said solution. I know that verbose/interactive mode is the default on some commands (I'm looking at YOU, scp!), which means you need to hadle said output for smooth operation of shell scripts. – Mange Jun 13 '12 at 12:09
2

If you edited your crontab file using a windows editor (via samba or something) and it's replaced the newlines with \n\r or just \r, cron won't run.

Also, if you're using /etc/cron.d/* and one of those files has a \r in it, cron will move through the files and stop when it hits a bad file. Not sure if that's the problem?

Use:

od -c /etc/cron.d/* | grep \r
Doug
  • 431
2

Line written in a way crontab doesn't understand. It needs to be correctly written. Here's CrontabHowTo.

pjmorse
  • 101
Kangarooo
  • 5,075
  • 1
    How can this be debugged? – Adam Matan Jan 27 '11 at 06:34
  • Reviewing cron's error log is the most common way. IIRC 'crontab -e' does a syntax parse after you've edited the file as well - but that might not be universal. – pbr Apr 08 '12 at 22:47
2

Although you can define environment variables in your crontable, you're not in a shell script. So constructions like the following won't work:

SOME_DIR=/var/log
MY_LOG_FILE=${SOME_LOG}/some_file.log

BIN_DIR=/usr/local/bin
MY_EXE=${BIN_DIR}/some_executable_file

0 10 * * * ${MY_EXE} some_param >> ${MY_LOG_FILE}

This is because variables are not interpreted in the crontable: all values are taken litterally. And this is the same if you omit the brackets. So your commands won't run, and your log files won't be written...

Instead you must define all your environment variables straight:

SOME_DIR=/var/log
MY_LOG_FILE=/var/log/some_file.log

BIN_DIR=/usr/local/bin
MY_EXE=/usr/local/bin/some_executable_file

0 10 * * * ${MY_EXE} some_param >> ${MY_LOG_FILE}
Alexandre
  • 3
  • 4
2

When a task is run within cron, stdin is closed. Programs that act differently based on whether stdin is available or not will behave differently between the shell session and in cron.

An example is the program goaccess for analysing web server log files. This does NOT work in cron:

goaccess -a -f /var/log/nginx/access.log > output.html

and goaccess shows the help page instead of creating the report. In the shell this can be reproduced with

goaccess -a -f /var/log/nginx/access.log > output.html < /dev/null

The fix for goaccess is to make it read the log from stdin instead of reading from the file, so the solution is to change the crontab entry to

cat /var/log/nginx/access.log | goaccess -a > output.html
  • This is a useless use of cat. You can use redirection instead of an additional process & a pipe: goaccess -a < inputfile > outpufile – arielf Mar 09 '22 at 21:40
2

Logging Permissions

A very simple crontab, won't execute because /var/log/ is not writable by luser account!

* * * * * /usr/local/bin/teamviewer_check >> /var/log/teamviewer.log 2>&1

SOLUTION: create the file as root and chmod to 777

Thanks to the previous suggestion of enabling cron logging in /etc/rsyslog.d/50-default.conf, leads to syslog entry (No MTA installed, discarding output), then thanks to "(CRON) info (No MTA installed, discarding output)" error in the syslog installing postfix in local mode, tail /var/mail/luser, /bin/sh: 1: cannot create /var/log/teamviewer.log: Permission denied

Kevin
  • 992
  • 9
  • 15
2

In my case cron and crontab had different owners.

NOT working I had this:

User@Uva ~ $ ps -ef | grep cron | grep -v grep
User    2940    7284 pty1     19:58:41 /usr/bin/crontab
SYSTEM   11292     636 ?        22:14:15 /usr/sbin/cro 

Basically I had to run cron-config and answer the questions correctly. There is a point where I was required to enter my Win7 user password for my 'User' account. From reading I did, it looks like this is a potential security issue but I am the only administrator on a single home network so I decided it was OK.

Here is the command sequence that got me going:

User@Uva ~ $ cron-config
The cron daemon can run as a service or as a job. The latter is not recommended.
Cron is already installed as a service under account LocalSystem.
Do you want to remove or reinstall it? (yes/no) yes
OK. The cron service was removed.

Do you want to install the cron daemon as a service? (yes/no) yes
Enter the value of CYGWIN for the daemon: [ ] ntsec

You must decide under what account the cron daemon will run.
If you are the only user on this machine, the daemon can run as yourself.
   This gives access to all network drives but only allows you as user.
To run multiple users, cron must change user context without knowing
  the passwords. There are three methods to do that, as explained in
  http://cygwin.com/cygwin-ug-net/ntsec.html#ntsec-nopasswd1
If all the cron users have executed "passwd -R" (see man passwd),
  which provides access to network drives, or if you are using the
  cyglsa package, then cron should run under the local system account.
Otherwise you need to have or to create a privileged account.
  This script will help you do so.
Do you want the cron daemon to run as yourself? (yes/no) no

Were the passwords of all cron users saved with "passwd -R", or
are you using the cyglsa package ? (yes/no) no

Finding or creating a privileged user.
The following accounts were found: 'cyg_server' .
This script plans to use account cyg_server.
Do you want to use another privileged account name? (yes/no) yes
Enter the other name: User

Reenter: User


Account User already exists. Checking its privileges.
INFO: User is a valid privileged account.
INFO: The cygwin user name for account User is User.

Please enter the password for user 'User':
Reenter:
Running cron_diagnose ...
... no problem found.

Do you want to start the cron daemon as a service now? (yes/no) yes
OK. The cron daemon is now running.

In case of problem, examine the log file for cron,
/var/log/cron.log, and the Windows event log (using /usr/bin/cronevents)
for information about the problem cron is having.

Examine also any cron.log file in the HOME directory
(or the file specified in MAILTO) and cron related files in /tmp.

If you cannot fix the problem, then report it to cygwin@cygwin.com.
Please run the script /usr/bin/cronbug and ATTACH its output
(the file cronbug.txt) to your e-mail.

WARNING: PATH may be set differently under cron than in interactive shells.
         Names such as "find" and "date" may refer to Windows programs.


User@Uva ~ $ ps -ef | grep cron | grep -v grep
    User    2944   11780 ?        03:31:10 /usr/sbin/cron
    User    2940    7284 pty1     19:58:41 /usr/bin/crontab

User@Uva ~ $
  • 1
    Although well documented, this looks like a Cygwin-specific point; does it really belong in askubuntu? – sxc731 Feb 07 '16 at 11:14
2

In 16.04, if you have this error in syslog

(CRON) error (can't fork)

try:

systemctl status cron.service

In the result:

Tasks: num_task (limit: 512)

If num_task is close to the limit use:

systemctl set-property cron.service TasksMax=new_max

Replace new_max with a suitable value.

heemayl
  • 91,753
2

Cron daemon could be running, but not actually working. Try restarting cron:

sudo /etc/init.d/cron restart
Eliah Kagan
  • 117,780
  • 3
    I've NEVER seen this case in production. Doesn't mean it hasn't happened - just that I've not seen it in the 30 years I've been using UNIX and Linux. Cron is insanely robust. – pbr Apr 08 '12 at 22:42
  • 1
    I'm not sure but I think this did actually just happen to me. I tried pidof cron and got nothing. Tried using service utility and it said cron was already running. Just ran this command and ran pidof again and I get a result. – Colleen Apr 06 '15 at 17:13
2

Cron jobs won't run if your user's password has expired.

One indication of this is if your cron log (typically /var/log/syslog on Ubuntu, I believe) contains messages like "Authentication token is no longer valid; new one required".

You can check for this problem by running sudo chage -l the_username.

Here's an example of an expired password:

$ sudo chage -l root
Last password change                    : password must be changed
Password expires                    : password must be changed
Password inactive                   : password must be changed
Account expires                     : never
Minimum number of days between password change      : 0
Maximum number of days between password change      : 14600
Number of days of warning before password expires   : 14

The fix is to change the password. For example:

sudo -u root passwd

Then follow the instructions, specifying a new password as prompted.

Thanks to https://serverfault.com/questions/449651/why-is-my-crontab-not-working-and-how-can-i-troubleshoot-it#comment966708_544905 for pointing me in the right direction here. In my case, just as for that commenter, it was the root user of a DigitalOcean box.

Henrik N
  • 1,536
2

On my RHEL7 servers, root cron jobs would run, but user jobs would not. I found that without a home directory, the jobs won't run (but you will see good errors in /var/log/cron). When I created the home directory, the problem was solved.

1

I had a problem with a script that called grep with a regular expression. Some regexs worked when the script was called from crontab while others did not, e.g. [[:print:]] did not work. It turns out that the environment variable LANG has an impact on character sets like [a-z] or [[:print:]]. When I pasted my LANG environment variable into the top of my crontab, i.e. "LANG=en_GB.UTF-8", the grep regular expressions all worked fine when the script was called from crontab. HTH.

1

If crontab mentions something like run-parts /etc/cron.daily, then run-parts may be refusing to run your script. In my case the script in cron.daily did not start with #!/bin/sh.

I discovered this by putting my script in a directory by itself and running run-parts against that directory.

1

This happened to me recently: I had two lines that modified PATH, like this:

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/home/ernesto/bin

Then, later in the file:

PATH=$PATH:/some/other/path

as one would typically do in a shell-interpreted context. However, $PATH seems to not have been expanded, causing all my jobs to fail. The solution is to put everything on a single line.

Edit:

Since I didn't want to wait until the next normal iteration of anacron to verify my jobs worked correctly, I ran:

anacron -fnd "jobname"

Where "jobname" is the job identifier specified in anacrontab. This forces jobs to be run by the same anacron process, sequentially, and without delay.

erjoalgo
  • 1,041
1

Another Gotcha:

When you type crontab -e and save inside the editor, it won't have any effect. You have to exit the editor for it to add or update according to your changes (e.g. use :x in Vim).

(crontab -e effectively runs "edit cron file; update from cron file" so it's blocking on the editor until you close it.)

mahemoff
  • 667
1

My crontab only worked when I was logged in as user.

I found the solution suggested here on Unix & Linux SE

What was the problem is that the scrips were in my home directory which was encrypted. So it will be unmounted and unavailable when I was logged of. You can use the command mount to see if your home directory is mounted for encryption.

I fixed the problem by putting my scripts in the /usr/local/bin folder.

d a i s y
  • 5,511
Paul
  • 101
1

After hours found out the problem where, I was editing the shell script from Windows (notepad++), where the file was originally located in a Linux server and I've missed out the new line character.

Since I was using Notepad++ to edit the file, I had to do the EOL Conversion to Unix in order to get the new line character, and it worked perfectly.

Hope it helps!

1

Another caveat:

Try not to put your cron scripts in a user's home directory.

I just had a problem where everything seemed good, but just after a while - probably a few hours after I log out, the cron jobs stops working, and these messages appear in the log:

Signature not found in user keyring
Perhaps try the interactive 'ecryptfs-mount-private'

My home directory, at least as far as I know, is not encrypted. But still, moving those scripts outside of the home dir solved the problem.

AlonL
  • 123
0

Crontab Permissions

There are two files that control the permissions for crontab: /etc/cron.allow and /etc/cron.deny. If there is a cron.allow file, then the user or users that need to use cron will need to be listed in the file. You can use cron.deny to explicitly disallow certain users from using cron.

If neither files exist, then only the super user is allowed to run cron.
Well, that depends on the system specific configuration to be exact. Most configuration do not allow any users to run jobs, while some systems allow all users to run jobs by default.

0

I once was working on a shared server with lots of restrictions.

All the answers here (PATH, SHELL, bash -c,...) could not get my script to work in the crontab.

It did work perfectly when I put the command in a little script file with the PATH, SHELL, and shebang rather than in the crontab itself. I did have to change the permissions to 700.

don.joey
  • 28,662
0

If running scripts within /etc/cron.* directories, make sure your scripts:

  • are executable,
  • match the Ubuntu/Debian cron script namespace (^[a-zA-Z0-9_-]+$).

So for example if you've script with extension (such as .sh) won't work.

To print the names of the scripts which would be run on hourly basis, try:

sudo run-parts --report --test /etc/cron.hourly
kenorb
  • 10,347
0

I had some issues with using sudo in a cron.

Basically I wanted to run a command as a specific user and I tested first, at the command line, su, which returned the error This account is not available. Using sudo, the command ran without errors.

However, when running from cron, the sudo command returned the following error: sudo: sorry, you must have a tty to run sudo.

I later found that using runuser and specifing a shell for the command to run, works.

marius-nyxpoint
  • 123
  • 1
  • 1
  • 7
0

Sometimes cron is working just fine but the script or command you want it to run just fails silently, causing you to bark up the wrong tree.

In such cases I find it useful to wrap the target within another short script, which outputs some visible debugging code (including output of date) and use redirection to ensure I get some evidence to inspect. If the target is a script, wrapping around a sh -x can help further.

The crontab entry (always edit with crontab -e) could look like this:

00 14 * * * (sh -x /tmp/my_wrapper_script) >> /tmp/debug.log 2>&1

Inspect /tmp/debug.log and its timestamp. If it's empty, non-existent or the timesamp isn't shortly after 14:00 (in this example) you may have a cron issue, otherwise you need to debug the actual target action and cron is working just fine!

sxc731
  • 1,134
0

You used:

*  *  * * * DISPLAY=:0 /path/to/your/app

but it failed because your ~/.dbus directory is owned not by you but by root. Check it:

ls -l ~/.dbus
loxaxs
  • 759
  • 5
  • 15