This article was published on August 25th 2014 and takes about 3 minutes to read.
Use it with caution — it is probably still valid, but it has not been updated for over a year.
Executing a scheduled Ruby script from within a cron job is a bit tricky if you manage your Ruby versions with rbenv. Let me show you how to do it right.
In one of my previous articles I created a small Ruby script to handle subscribers to your newsletter in the background.
The way I executed this script from within my crontab caused a little confusion among some readers, so let me elaborate a bit on why you cannot just run a Ruby script from within a crob job when you are using rbenv (examples are run as
app user on my production server).
By adding the crontab entry
*/1 * * * * env > /tmp/env.txt you'll get a dump of all environment variables into the file
/tmp/env.txt every minute. After waiting for a minute, let's take a peek into this file:
... PATH=/usr/bin:/bin ...
Compare this to the output of
env run from inside a normal shell:
... PATH=/home/app/.rbenv/shims:/home/app/.rbenv/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/app/bin ...
You can see that the
$PATH variable (listing all directories where your system looks for executable files) is very stripped down and does not include the directories needed for rbenv to work (adding
$HOME/.rbenv/bin to your
$PATH is done as part of the rbenv installation if you refer to its documentation).
The solution to this problem is opening a non-interactive login shell from within the cronjob and run the script this way:
*/1 * * * * bash -lc "env > /tmp/env.txt"
... PATH=/home/app/.rbenv/shims:/home/app/.rbenv/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/app/bin ...
$PATH is configured the way it needs to be in order for rbenv to take over. In most cases (at least on CentOS/RedHat-based systems) this is sufficient to make the following crontab entry work:
*/1 * * * * bash -lc "ruby script.rb"
However, there may be cases where your
bash does not reside in
/bin but in some other place like
/usr/local/bin. Since the user's
$PATH will not cover that location, you cannot even open a login shell.
In this case, you have to add this directory to the
$PATH manually before opening a login shell. First, find out where your
bash command lives by running the command
Then add this directory to the user's
$PATH before opening the login shell from within the cron job:
*/1 * * * * PATH=$PATH:/usr/local/bin && bash -lc "env > /tmp/env.txt"
If you find yourself in constant need to run scheduled Ruby scripts, it might pay to create a small wrapper script to keep things DRY.
Create a script called
~/bin (because as we have seen above, this place is always covered within
#! /bin/sh PATH=$PATH:/usr/local/bin bash -lc $*
After making it executable with
chmod +x ~/bin/rbenv_wrapper this script adjusts the
$PATH and the runs any command(s) you supply inside a non-interactive login shell.
Now you can run Ruby scripts from within your crontab like this:
*/1 * * * * rbenv_wrapper script1.rb */1 * * * * rbenv_wrapper script2.rb > /tmp/output.txt