This article was published on August 25th 2014 and received its last update on March 28th 2019. It takes about 3 minutes to read.
Running Ruby scripts from within a cron job in an rbenv environment
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.
Table of contents
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).
The cron environment
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/shims
and $HOME/.rbenv/bin
to your $PATH
is done as part of the rbenv installation if you refer to its documentation).
Run the script from within a non-interactive login shell
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"
The output:
...
PATH=/home/app/.rbenv/shims:/home/app/.rbenv/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/app/bin
...
Now our $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"
Still no luck?!
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 which bash
.
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"
Creating a wrapper script
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 rbenv_wrapper
in ~/bin
(because as we have seen above, this place is always covered within $PATH
):
#! /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 then 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
Get in the loop
Join my email list to get new articles delivered straight to your inbox and discounts on my products.
No spam — guaranteed.
Got it, thanks a lot!
Please check your emails for the confirmation request I just sent you. Once you clicked the link therein, you will no longer see these signup forms.