Skip to content

HOWTO devops rvm_openssl_issue

steveoro edited this page Jan 9, 2025 · 2 revisions

HOW-TO: solve issues setting-up, compiling or using RVM Ruby with OpenSSL depending on version

Any projects that adopts Ruby 3.1+ shouldn't face any issues with OpenSSL 3+ as it's now supported by Ruby itself.

Issues may arise when using Ruby 3.0 or older versions with RVM on Ubuntu 22+ due to the included OpenSSL 3.

Using Ruby 3 with RVM on Ubuntu 22+

When attempting to compile Ruby 3 with RVM on Ubuntu 22+, you might encounter issues due to the included OpenSSL version.

Here are some options for addressing the problem:

  1. Downgrade OpenSSL and libssl-dev: downgrading the OpenSSL and libssl-dev packages can resolve the compilation issue. Use the following commands to downgrade:

    sudo apt install libssl-dev=1.1.1l-1ubuntu1.4 openssl=1.1.1l-1ubuntu1.4
  2. Install OpenSSL with RVM: You can install OpenSSL using RVM's package manager and then specify the OpenSSL directory during the Ruby installation. Use these commands:

    rvm pkg install openssl
    rvm install ruby-3.0.0 --with-openssl-dir=$HOME/.rvm/usr
  3. Reinstall RVM: If the above steps do not work, you might need to reinstall RVM. First, remove RVM and then reinstall it:

    rm -rf ~/.rvm
    curl -L https://get.rvm.io | bash -s stable
  4. Check RVM Version: Ensure you are using the latest version of RVM. Update RVM to the latest version with:

    rvm get master

Note that there are some gems, specifically most DB drivers, that may throw a seg-fault when trying to access the DB due to the OpenSSL version mismatch.

Typical case: build the application bundle may succeed, but the application will crash as soon as it's trying to access the DB. (See more below.)


Installing legacy Ruby & legacy Rails versions on Ubuntu 24+

Deployment notes:

The content of this page is merely a developer workstation-oriented solution and differs from what may be needed to actually deploy the application.

But whenever the deploy is done using scripts that rely on rvm and the target server is running on newer Ubuntu machines, changing the Ruby version manager needs some housekeeping.

Keep in mind that rvm can't coexist easily with rbenv or any other version manager, especially when installed at system level.

So, the best course of action is to remove rvm and update the deploy scripts to use the chosen version manager instead. (Unless deploying to a legacy server that still uses rvm or using other means like Docker or any other VMs.)

Remove rvm first with rvm implode (when installed at user level) and update all deploy scripts to the alternative capistrano+rbenv ecosystem.

Update gem groups:

group :development do
  gem 'capistrano', '~> 3.10' # or whatever
  gem 'capistrano-rails', '~> 1.3'
  gem 'capistrano-rbenv', '~> 2.1'
end

Edit the capfile to include the dependencies:

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Possible deploy.rb updates:

set :rbenv_type, :user
set :rbenv_ruby, '<2.x.x>' # the specific version you want to use
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all

Compiling legacy Ruby with RVM

Any very-old legacy Ruby (e.g., any v2.x.x) can still be installed on newer Ubuntu versions even if they ship with a newer OpenSSL 3+, using rvm as done for Ruby 3.0:

$ rvm pkg install openssl
$ rvm install ruby-2.7.4 --with-openssl-dir=$HOME/.rvm/usr

(REMINDER: NEVER do a system-wide install for rvm using the apt package and rely exclusively on user-level install with curl so that rvm implode may actually clean the setup in case issues arise.)

Unfortunately, any additional gem that relies on OpenSSL, as stated above, will either HALT the binstub compilation during bundle install due to a library mismatch, or due to a misconfiguration (i.e.: legacy ruby w/ custom openssl v1 support VS system's openssl v3+) or, possibly, will complete the bundle anyway but throw a seg-fault as soon as the DB gets accessed using the compiled driver binaries.

Alternatives to RVM

Best alternatives for RVM are its "low-level" counterparts:

  • rbenv
  • asdf
  • mise, supported as new default versioning system by Rails 8+

asdf is slightly slower than rbenv but, similarly to mise, allows to isolate even JS & python through plugins and, allegedly, has a better install experience than rbenv. mise is a sort of wrapper around asdf and it seems to be faster.

In any case, choosing rbenv here for reasons of personal experience.

Compiling legacy Ruby with rbenv

References:

Install the latest rbenv with its ruby-build plugin:

$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ ~/.rbenv/bin/rbenv init
$ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

Do plugin upgrades with git -C "$(rbenv root)"/plugins/ruby-build pull and git -C ~/.rbenv pull when needed.

On a typical system, these are the required build env libs dependencies:

$ sudo apt-get install autoconf patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev

Additional apt dependencies for building the binstubs: (do a sudo apt install for each additional library)

  • Postgres/pg => postgres, libpq-dev
  • any globalization or Unicode libraries, like charlock-holmes => libicu-dev
  • rmagick => libmagickcore-dev
  • any JS VM engine, like execjs => nodejs
  • Redis or resque => redis
  • nokogiri => libxml2-dev, libxslt1-dev
  • mongo => mongodb

In any case, if the bundle halts during the binstub compilation, take note of the names of any missing libraries reported in the errors and add them (the -dev version with the headers) to the required bindings of the new setup.

(This is true for any version manager, including the usual, RVM.)

Install legacy Ruby with rbenv

References:

Running ruby-build without any additional parameters will also download & install into a temporary folder the required version of openssl (1.1.1) needed by the ruby compilation (but this temporary install won't be reused during the bundle compilation later on).

This won't prevent possible errors during the bundle phase raised by drivers relying on a specific openssl version and, even less, won't prevent the application from throwing seg-faults later on.

The best approach is to use a "permanent" local build of OpenSSL, reused every time there's a new dependency binding for the gem bundle.

Do a local build for OpenSSL under ~/.openssl/openssl-1.1.1w with:

$ wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz
$ tar zxvf openssl-1.1.1w.tar.gz

$ export OPENSSL=$HOME/.openssl/openssl-1.1.1w

$ cd openssl-1.1.1w
$ ./config --prefix=$OPENSSL --openssldir=$OPENSSL
$ make
$ make test
$ make install

As this doesn’t include any certificates, symlink the main (system-wide) OpenSSL certs folder into this version like so:

rm -rf $OPENSSL/certs
ln -s /etc/ssl/certs $OPENSSL/certs

Install Ruby with: (remember that $OPENSSL must be always set before this)

# Ruby 3.0
RUBY_CONFIGURE_OPTS=--with-openssl-dir=$OPENSSL rbenv install 3.0.7

# Ruby 2.7
RUBY_CONFIGURE_OPTS=--with-openssl-dir=$OPENSSL rbenv install 2.7.x

# Ruby 2.6
RUBY_CONFIGURE_OPTS=--with-openssl-dir=$OPENSSL rbenv install 2.6.10

# Ruby 2.5
RUBY_CONFIGURE_OPTS=--with-openssl-dir=$OPENSSL rbenv install 2.5.9

Set a Ruby version to finish installation and start using Ruby:

$ rbenv global 2.7.x   # set the default Ruby version for this machine
# or:
$ cd <project_root>
$ rbenv local 2.7.x    # set the Ruby version for this directory

# Verify versions:
$ cd <project_root>
$ ruby -v

# Check that the system's openssl is still v3:
$ openssl version

Check the location where gems are going to be installed with gem env:

$ gem env home
# => ~/.rbenv/versions/<version>/lib/ruby/gems/...
Proceed to install gems as you normally would (for 2.7.x):
$ gem install bundler -v 2.4.22
$ bundle install

Final reminders:

Note that a specific version of Bundler is needed for very-old Ruby versions like the 2.7.x.

In case rails -T doesn't work yet, do a rbenv rehash when changing to project folder.

Never ever use sudo to install gems.

Clone this wiki locally