Why You Should Never Use sudo to Install Ruby Gems

Have you ever tried to install a Ruby gem and gotten an error like the one below?

ERROR: While executing gem ... (Gem::FilePermissionError)
You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory

Or maybe "command not found", or Failed to build gem native extension?

These errors are common because macOS (and previously OS X) doesn't come out of the box with a proper Ruby development environment. There is an easy, quick, and safe solution, but unfortunately most people don't know about it, or try to make it more complicated than it should be, or worse, they tell people to use the dangerous sudo command without understanding the implications.

What is so dangerous about using sudo to install gems? Well, gems can run code during installation, so if you think that using sudo gem install is a normal, perfectly fine thing to do, then you might use it to inadvertently install a malicious gem that could potentially wipe out your entire hard drive.

You know how macOS prompts you for your password when you install certain applications, or when you try to change certain System Preferences? That's to protect you. When you first type sudo gem install, you will be prompted for your password, but once you enter it, anything else that would normally require your password will automatically be granted access. You're telling the gem, "Go ahead, do whatever you want to my computer!"

So, if we're not supposed to use sudo to install gems, what's the right way? There are various safe ways to install gems on a Mac as I have written about in great detail. There is only one I recommend, though, and that's with a Ruby manager, such as RVM, rbenv, chruby, or asdf. This is the most flexible option, and sets people up for success for the long term. I prefer chruby because it's the lightest, and easiest to use.

However, installing a Ruby manager on its own is not enough. It's only one of several steps needed for a complete working Ruby development environment. Luckily, all the steps can be automated. I subscribe to the philosophy that if a process can be made faster and less painful, it should be. There's no reason to confuse people with complicated, error-prone manual steps, or to make them choose between various ways to install Ruby. Just pick one reliable way, and automate it! This is why I wrote a script to automate the setup of a Ruby development environment on a Mac. I've tested it many times, and it works perfectly on a clean macOS that hasn't had previous attempts at setting up development tools. If you've previously attempted to set up a development environment and you run into any issues with my script, I will be happy to help you fix them.

Now that it's clear why you shouldn't use sudo to install gems, I'm asking you to please spread the word. If you ever come across someone recommending sudo gem install, please politely teach them the better, safer way. And if you find tutorials that require folks to manually run a bunch of commands to set up a proper Ruby environment, ask them to consider providing a script instead. I don't care if it's my script or someone else's. As long as it's repeatable and reliable, that's what matters.

If you enjoyed this post, and want me to help you on your coding journey, subscribe to my newsletter. I love to hear from my readers, so please reach out. I'm monfresh everywhere.

P.S.

Not convinced? Let's prove it together by building our own gem locally. First, we'll create a new folder to hold our gem. Let's call it sudogem. Run the following commands in your terminal:

cd ~
mkdir sudogem && cd sudogem

Let's create some files:

touch extconf.rb
touch sudogem.gemspec

Open the sudogem folder in your favorite text editor.

In extconf.rb, paste this harmless code that just prints various messages, and save the file:

#!/usr/bin/env ruby

$stderr.puts "Hello, this is code running during gem installation"
if Process.uid == 0
  puts "YOU HAVE BEEN PWNED."
else
  puts "Stay safe!"
end

In sudogem.gemspec, paste the following and save the file:

Gem::Specification.new do |s|
  s.name        = "sudogem"
  s.version     = "0.0.0"
  s.authors     = ["Monfresh"]
  s.email       = ["sudo@gem.com"]
  s.homepage    = "https://www.moncefbelyamani.com/"
  s.summary     = "DO NOT INSTALL THIS GEM. IT'S JUST A TEST."
  s.description = "Demonstrates how you can run any code during installation."
  s.license = 'MIT'

  s.extensions   = ["extconf.rb"]
end

Build the gem:

gem build sudogem.gemspec

You should see this result:

Successfully built RubyGem
Name: sudogem
Version: 0.0.0
File: sudogem-0.0.0.gem

Now install it with sudo:

sudo gem install ./sudogem-0.0.0.gem

This will prompt you for your password. After you enter it, you should see some failures while building native gem extensions, and then you should see this:

Hello, this is code running during gem installation
YOU HAVE BEEN PWNED.

Now try installing it without sudo, and you should see this:

Hello, this is code running during gem installation
Stay safe!