www.jdmz.net troy.jdmz.net

Multiple Ruby Installations
Why Would You?
I am a sysadmin. I like to do strange things. Maybe you do too?
It turns out that having your own custom Ruby, or even a few of them, may not be very costly. I'll use an example with a few different Ruby versions.
Ruby 2.6.10
For Ruby 2.6.10 I did this:
sudo mkdir -p /usr/local/ruby/build /usr/local/ruby/2.6.10
sudo chown $USER:$USER /usr/local/ruby
cd /usr/local/ruby/build/
wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.10.tar.gz
tar xzvf ruby-2.6.10.tar.gz
cd ruby-2.6.10
./configure --prefix=/usr/local/ruby/2.6.10
make && make check && make install
/usr/local/ruby/2.6.10/bin/gem install rails
But the rails install had an error because activesupport requires ruby 2.7.0 or higher.

Ruby 2.7.6
For Ruby 2.7.6 I did this:
sudo mkdir -p /usr/local/ruby/build /usr/local/ruby/2.7.6
sudo chown $USER:$USER /usr/local/ruby
cd /usr/local/ruby/build/
wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.6.tar.gz
tar xzvf ruby-2.7.6.tar.gz
cd ruby-2.7.6
./configure --prefix=/usr/local/ruby/2.7.6
make && make check && make install
/usr/local/ruby/2.7.6/bin/gem install rails

Ruby 3.0.4
For Ruby 3.0.4 I did this:
sudo mkdir -p /usr/local/ruby/build /usr/local/ruby/3.0.4
sudo chown $USER:$USER /usr/local/ruby
cd /usr/local/ruby/build/
wget https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.4.tar.gz
tar xzvf ruby-3.0.4.tar.gz
cd ruby-3.0.4
./configure --prefix=/usr/local/ruby/3.0.4
make && make check && make install
/usr/local/ruby/3.0.4/bin/gem install rails

Ruby 3.1.2
For Ruby 3.1.2 I did this:
sudo mkdir -p /usr/local/ruby/build /usr/local/ruby/3.1.2
sudo chown $USER:$USER /usr/local/ruby
cd /usr/local/ruby/build/
wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.2.tar.gz
tar xzvf ruby-3.1.2.tar.gz
cd ruby-3.1.2
./configure --prefix=/usr/local/ruby/3.1.2
make && make check && make install
/usr/local/ruby/3.1.2/bin/gem install rails

Common Flexible Binary

To make the different versions of Ruby easy to use and change, the Ruby binaries have to be in the PATH. Typically /usr/local/bin/ is in the path, so that is where I want to put my Ruby binaries. Because I want to use my custom Ruby binaries by default, I will check my path and make sure /usr/local/bin/ comes before /usr/bin/ (or wherever the packaged Ruby binaries are located) [1].

I will then create a default Ruby symbolic link at /usr/local/ruby/current and point it at the Ruby version I want to use by default.

su -
cd /usr/local/ruby
ln -s 3.0.4 current
ln -s 3.0.4 current3
ln -s 2.7.6 current2
ln -s 3.1.2 test
I also want the other versions to be available without typing the full path, so I will do this:
cd /usr/local/bin/
for f in /usr/local/ruby/2.7.6/bin/* ; do echo $f ; b=`basename $f` ; ln -s $f ${b}276 ; done

but, then again, typing the full path is not too hard (and that is a lot of links). You be the judge if that is a useful thing for command comparisons or not. I will then make the default binary links:

cd /usr/local/bin/
for f in /usr/local/ruby/current/bin/* ; do echo $f ; b=`basename $f` ; if [ ! -f $b ]; then ln -s $f $b ; fi ; done

The if avoids name collisions with version specific binaries. Keep in mind than you may want to limit what you put into /usr/local/bin/, or at least make it easy to clean up (like with a 'rm *276' or something).

Keeping Ruby Up To Date

If you don't care, and just want the latest of everything, just do this:

/usr/local/ruby/2.6.10/bin/gem update
/usr/local/ruby/2.7.6/bin/gem update
/usr/local/ruby/3.0.4/bin/gem update
/usr/local/ruby/3.1.2/bin/gem update

Each process could take a considerable amount of time. Again, I often use screen to make it easier on me.

Managing Ownership Of Ruby

I like having my Ruby installations owned by someone other than root. Running a ton of code as root can be avoided here, and so it should [2].

On my own personal servers, I don't mind that person being me (but I see that it could be a risk), but that arrangement may not work for groups larger than one.

For those situations, I think making a rubyadm user and group might serve:

sudo groupadd rubyadm
sudo useradd -m -g rubyadm -c "Ruby Install Administrator" rubyadm

Then apply the permissions to the installations:

cd /usr/local/
sudo chown -R rubyadm:rubyadm ruby

When you need to update or change an installation in some way, you will then want to become the 'rubyadm' user with su or sudo.

Notes:
[1] Different shells want the PATH set in different places. You'll probably be modifying a file within the /etc/ directory to change the default path for the entire system.
[2] The more limited the user, the less risk. I want my updates to work within the filesystem tree it owns and not outside it.
© 2022 Troy Johnson
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license should be available here and here.
The current copy of this document should be available here.