cron job not working

I have set up my cron job as following:

0 0 1 * * certbot renew

while as a root user (switched to root using "su -l root") using the "crontab -e" command. I have further confirmed that it does appear in the file "/var/cron/tabs/root".

However, the cron command is never fired. Why? And, how can I resolve the problem?
First: check your e-mail. Whenever there's a problem with cron which triggers output then that will be sent to the job owner (root in your case).

But also: where exactly is certbot located? Not all paths are available within a crontab context, so my advice would be to specify the full pathname just to be sure.


Same problem as always, cron(8) has a limited path which doesn't include /usr/local/bin.
That's not correct … The default PATH for jobs executed via cron is set to /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin. See the crontab(5) manual page.

To analyze the problem, you should first make sure that email delivery works for the output from cron jobs. To do that, temporarily create an entry * * * * * nonexist. It should create an email message every minute. If it doesn't, fix your email setup first. If you can't fix it, an alternative way to get the output would be this:
0 0 1 * * /bin/sh -c "certbot renew" >/root/crondebug.output 2>&1
Note that you should not write the file to /tmp. Use a directory that is only writable by yourself (in this case, root).

Now, if the cron job produces any error messages, they should be written to the file given, indicating the cause of the problem.

That leaves only the case that the command is started successfully by cron, but the command itself fails silently. That will be more difficult to debug. If certbot is a shell script, you could start the shell with the -x option to get a list of the actual commands executed, so you can see at which point it is failing. Similar ways exist for other scripting languages (Python, Perl, …).

If it's a binary, it's more difficult. Check if the documentation mentions any debug or verbosity options. If everything else fails, run the program with truss(1) or ktrace(1) to see if any system calls are failing, but the output might be difficult to read for non-developers.
An additional technique to help with debugging and running cron jobs: Take whatever program you run, and wrap it in a shell script. Design the script such that in the normal case (success), it creates absolutely no output on either stdout or stderr. Like that you get no e-mail if everything is working. What to do when something goes wrong? There are two schools of thought: (1) Let the command run normally, and cron will mail the potentially complex output to the user. (2) Catch all the output in the script, and store it in a well-known location like /var/log. Then in case of failure, output just one line: "foobar failed, please see /var/log/foobar_20181214.error.log for details".

In this particular case: certbot is a python script, which probably relies on certain environment settings to find its libraries and configuration. Cron deliberately strips the environment for safety reasons. This could easily cause certbot to fail, perhaps silently if the e-mail output from cron is broken.


The default PATH for jobs executed via cron is set to /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin. See the crontab(5) manual page.

By the way, the above is true for regular cronjobs. For /etc/crontab, the default PATH did not include /usr/local until recently. It was fixed in current about 6 months ago (click here), and this change was inherited by FreeBSD 12. It was MFC'ed to stable/11 just yesterday.