Prasanna Natarajan

Installing ruby using rvm offline

Here you’ll learn how to install ruby offline in your servers using rvm, using the missing step from the official documentation.

Let’s say you have an infrastructure where every time you deploy, you provision a blank server and then proceed to install all the required dependencies for your rails app, before actually installing the rails app itself.

One of the steps would be to use a ruby version manager to install the precise version that your app requires. Eg: rvm. So, one of the steps in preparing the server would be to first download and install rvm itself, and then download and install the required ruby. You could find the required steps at https://rvm.io/rvm/install#installation. But notice that this means relying on external servers to be up and running to download the necessary files. This is risky.

It’s better to have the required files for rvm and the rubies downloaded already in your local servers, and then have the server setup script to just run the installation commands on the files. This way you save yourself from the network risks, and also the whole step is much faster.

RVM has a documented page showing how to do the same: https://rvm.io/rvm/offline.

The steps shown there are a bit non-linear. So here they are, slightly re-arranged.

You’d first have to make sure to know about, and install the rvm dependencies. So get a server to play, or use docker to provision the specific OS version that your project requires, and then install rvm normally, using the steps mentioned in https://rvm.io/rvm/install#installation. Then run rvm requirements, which will list all the required packages. Note them down and make sure your server setup script installs them before proceeding to setup ruby.

(These requirements might be autoconf, automake, bison, libffi-dev, libgdbm-dev, libncurses5-dev, libsqlite3-dev, libtool, libyaml-dev, pkg-config, sqlite3, zlib1g-dev, libgmp-dev, libreadline6-dev, libssl-dev)

Then, the steps would be to,

Download and install rvm first

Download latest rvm using curl -sSL https://github.com/rvm/rvm/tarball/stable -o rvm-stable.tar.gz.

Install rvm from this file using steps mentioned in this specific subsection: https://rvm.io/rvm/offline#installing-rvm-offline

(Note: If your script runs as root, then rvm will be installed at /usr/local/rvm. So, you should change the “Load rvm” step to source /usr/local/rvm/scripts/rvm)

Download the required ruby and the dependencies (rubygems, yaml, bundler)

  • Download the tar.bz2 version of the ruby your project requires from https://ftp.ruby-lang.org/pub/ruby/. The curl command is: curl -sSL https://ftp.ruby-lang.org/pub/ruby/ruby-2.2.0.tar.bz2 -o ruby-2.2.0.tar.bz2
  • Download the required rubygems using curl -sSL http://production.cf.rubygems.org/rubygems/rubygems-2.4.6.tgz -o rubygems-2.4.6.tgz
  • Download the required yaml using curl -sSL http://pyyaml.org/download/libyaml/yaml-0.1.6.tar.gz -o yaml-0.1.6.tar.gz.
  • Download the required bundler gem from rubygems website: https://rubygems.org/gems/bundler/versions/

Since rvm is already installed at this point, you’ll have access to the $rvm_path var. Check it using echo $rvm_path. It should return some path depending on how you installed rvm (as root or as some other non-root user).

So now, copy these files into the rvm archives folder: $rvm_path/archives/.

Remaining setup

Now, disable automatic dependencies (“requirements”) fetching using rvm autolibs read-fail.

And then run these:

echo "" > ~/.rvm/gemsets/default.gems
echo "" > ~/.rvm/gemsets/global.gems

(Change the path to $rvm_path/gemsets/ if you’re running as root)

The caveat(s)

Here’s where the rvm offline documentation failed for me. It says to go on and install the required ruby using rvm install like so:

rvm install 2.2.0 --rubygems 2.4.6
rvm use 2.2.0 --default

But then I noticed that this doesn’t lookup the already downloaded rvm binary in the archives path. Instead it just downloads it from the internet. This is what we are trying to avoid actively!

So, the suggested solution (not officially) was to use rvm mount like so:

rvm mount $rvm_path/archives/ruby-2.2.0.tar.bz2

But then, this too won’t work. It won’t be able to find the bin/ruby file, and so will complain “missing ruby”.

The problem is, the step requires yet another file - bin-ruby-2.2.0.tar.bz2 - in the archives folder. The documentation doesn’t say this, and also I didn’t know where to download this file from. But there’s a way to get this file easily.

Install ruby using rvm install first in docker or in some scratch server. Now, if you go to the rvm archives folder, this file will be present. Take it and put it in the archives folder, along with the other files above.

The payoff

Now if you run try to install ruby with the rvm mount command above, it will succeed! More importantly, there won’t be a network lookup and download. The command will just install from the files present in the archives folder.

(Even now, during the ‘#setup’ phase, you might see an error “Missing ‘ruby’ in ‘rubygems_detect_ruby_lib_gem_path’”. I can’t find a fix for this, but despite this, the correct ruby and rubygems versions will be installed and everything will just work.)

Update

I’ve opened a github issue describing this problem: https://github.com/rvm/rvm/issues/4672.

I’m also planning to see if I can understand the rvm source to see how rvm install gets the bin-ruby-* file and see if I could incorporate it.