Running rake tasks via cron can be frustrating as often it doesn’t work in the way you expect and often won’t give you any clues as to why. Here is the job I use in my crontab to run Rails rake tasks, plus a bunch of things to try while troubleshooting your own.
The following job will run every minute and send the output of the tasks to
/var/tmp/cron.log
.
* * * * * /bin/bash -l -c 'cd /var/www/my-rails-project && RAILS_ENV=production /home/planetroast/.rbenv/shims/bundle exec rake my-rake-task > /var/tmp/cron.log 2>&1'
Setting the cron job’s time incorrectly will introduce red herrings so for the purpose of troubleshooting let’s set it to run every minute. We do this with 5 asterisks before the command.
* * * * * rest-of-your-cron-job-goes-here
The root cause of my tasks failing is often because cron runs in a minimal environment meaning it might not have access to the tools or variables your job needs to run.
We can get around this by using the -l
flag which will invoke a full login
shell providing access to your path and all settings in your bashrc
. Note that
when using this flag we must also pass the command in using the -c
flag.
One reason my jobs were failing was because cron didn’t have access to bundler
so kept raising a bundle: command not found
error. This is due to cron running
in a minimal environment, you can fix it by getting the full path and using it
in your cron job.
user@planetroast:~$ which bundler
/home/planetroast/.rbenv/shims/bundler
If your cron job requires multiple command then cramming it all into one line in the crontab isn’t ideal. It can be better to create a bash script where you can write the commands clearly and easily edit them. This also allows you to easily run the script for testing without having to wait for the clock to tick over.
# Set working directory
cd /var/www/my-rails-app || exit 1
# Run the rake task
RAILS_ENV=production /home/planetroast/.rbenv/shims/bundle exec rake my-rake-task
* * * * * /bin/bash /home/planetroast/my-wrapper-script.sh
If nothing seems to work then make a temporary file /var/tmp/cron.log
and set
up a simple cron job which echos some text to a temporary file.
* * * * * echo "Hello from crontab" >> /var/tmp/cron.log
Now you can cat /var/tmp/cron.log
to make sure that cron is running.
There is a distinction between using /bin/sh
and /bin/bash
to run your cron
jobs. By default cron using /bin/sh
to run jobs but you’re probably going to
need bash if you’re running rake tasks. I’m not yet sure exactly why this is but
it seems to be a common reason that cron jobs fail.
If your cron job is running a script you can check the syslog to ensure it is running:
grep my-script.sh /var/log/syslog