Using RVM within a cron job

December 16, 2011

This post was originally published in the Rambling Labs Blog on December 16, 2011.

Long time no post! I’ve been really busy these days with some projects. Yet, here I am again, so here it goes.

A couple of days ago, I started working on a big story in one of those projects I’m currently working on. It’s one of those things that needs to be run a couple of times a day… yeah, a scheduled background job.

Basically, what it need to do is check some database records, check their expiration date against the current date, and take some actions for each one of the expired ones. Simple enough, right?

I went ahead and wrote a rake task that would do the heavy work. Something like this:

task :check_expired_records do
  Record.where('expired = ? AND expires_at < ?', false, do |record|
    # send some emails
    # ...
    # take some other actions
    # ...
    record.expired = true

Then, I was going to add a line to the crontab to run it when scheduled, which I thought would be the easiest part. I added a line like this:

0 */12 * * * cd /path/to/the/records_app/ && rake check_expired_records

To my surprise, it didn’t work. Checking the /var/log/syslog file, I saw this perplexing message:

error (grandchild #4993 failed with exit status 127)

Doing some extra debugging (redirecting both stdout and stderr outputs to a file), I found out that the problem was rake not being found.

Hmm, that must mean that rvm isn’t being loaded, right? I tried this instead:

0 */12 * * * rvm use 1.9.2 && rvm gemset use records_app cd /path/to/the/records_app/ && rake check_expired_records

Same error… But now it doesn’t find rvm. Let’s load it to the path manually:

0 */12 * * * PATH=$PATH:$HOME/.rvm/bin && rvm use 1.9.2 && rvm gemset use records_app && cd /path/to/the/records_app/ && rake check_expired_records

Ugh, yet another error message… Gemsets cannot be set when not using rvm rubies (currently).. Ok, let’s add the gems path to our path as well:

0 */12 * * * PATH=$PATH:$HOME/.rvm/bin:$HOME/.rvm/gems/ruby-1.9.2-p290@records_app/bin && rvm use 1.9.2 && cd /path/to/the/records_app/ && rake check_expired_records

Nothing!… Now the error is a GemNotFoundExceptionSay what??. Hmm, taking into account all previous errors, it’s definitely an environment issue.

After some googling, I found this post which pretty much explained how to do what I was trying to do. Turns out that the cron job is executed with a very limited environment. That means that in order for rvm to work correctly I have to fire up another bash with the correct environment, load rvm and finally execute what I want to.

So, at last, I ended up with this line in the crontab file:

0 */12 * * * /bin/bash -l -c 'source "$HOME/.rvm/scripts/rvm" && rvm use 1.9.2 && rvm gemset use records_app && cd /path/to/the/records_app/ && rake check_expired_records'

Awesome! It works! Now that was intense!