The definitive guide to installing Ruby gems on a Mac

A very common error people run into when trying to install any Ruby gem on macOS is a permissions error much like this one:

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

This can be very frustrating for newcomers, especially when the proposed solutions are inconsistent, wrong, or potentially dangerous. People face this error mainly due to incomplete documentation. As developers become more experienced, they tend to take certain things for granted, and forget to cultivate a beginner's mindset. Even popular gems like Rails are not immune to this. The Rails installation instructions imply that as long as you can run ruby -v and get version 2.5.0 or later, gem install rails should work, but it does not out of the box on a new macOS computer. It results in the error message above. This is the case for any gem you try to install on a Mac that has not yet been properly configured for Ruby development.

macOS returns this error because the default location for Ruby gem installations is the system Ruby directory that is preinstalled by Apple. That directory is not meant to be modified, hence Apple not giving us permissions to write to it.

So how can we install gems?

There are two main options: 1) install a separate version of Ruby that you control, or 2) keep using the system Ruby via some modifications. Option 1 can be further split into two options: install the new version with Homebrew, or with a Ruby manager.1

I highly recommend using a Ruby manager because it allows you to have multiple versions of Ruby installed at the same time, and makes it easy to switch between them. Even if you are using Ruby for the first time, it is worth your time to learn how to use a Ruby manager because you will inevitably need one as you progress.

Since the Ruby manager option is the one I recommend, we'll go over it first, and then I'll describe the other options so you can appreciate how complicated this can get, and why I wrote a script to automate it all.

Installing Ruby with a manager

At a high level, there are a minimum of five steps to a working Ruby environment on macOS with a Ruby manager:

  1. Install Homebrew (which also installs the prerequisite Apple command line developer tools)
  2. Install a Ruby manager
  3. Configure the Ruby manager
  4. Install a specific version of Ruby
  5. Switch to that version of Ruby

Luckily, these can all be automated, as I have done in my install-ruby-on-macos and laptop scripts. The former only installs the minimum needed to use Ruby, and is meant for folks who know that's all they need. The latter is what I recommend for most people, as it installs additional useful and necessary development tools, and is customizable.

Ideally, every maintainer of a Ruby project would point their users to a script that can set everything up for them. Not only does it save time, but it virtually eliminates errors — and therefore discouragement — at such a critical time for beginners. It also saves the maintainers time thanks to a decrease in issues reported by users who are running into the permissions error.

In Badass: Making Users Awesome, Kathy Sierra makes two very important points:

Working on what stops people matters more than working on what entices them.

and

What's much much worse than a bad user manual? Making the user think the manual works just fine for everyone else.

Sadly, many projects don't provide prerequisite setup instructions. If they do, they are typically missing steps (as we've seen with Rails, or Sinatra, or Hanami), or the instructions are provided in an outdated video that doesn't match the written ones, or the user has to choose between various options, all of which involve manually running commands.

One project that stands out that does provide a laptop style script is Discourse2. Given how reliable such scripts are, and how easy it is to point to them, or to fork them and write your own, there's little reason not to. I also applaud those who have taken the time to write step by step documentation, as it's a great first move. In fact, that's how I started this blog in 2012. Three years later, on my first day at 18F, I followed my own manual instructions to set up my work laptop. That's when it hit me that this should be automated, and how much time would be saved if every new public servant would receive their laptop already configured. 43 days later, 18F/laptop was born, inspired by thoughtbot's laptop script. It has now been almost two years since I left 18F, and the script still continues to be used and maintained.

Another advantage of these laptop scripts is that they can be run over and over safely, and they keep your system up to date. They only install what you don't already have, and they upgrade what's outdated. One thing to watch out for is any incompatibility between what you might already have installed and what the script installs. For example, the three most popular Ruby managers — RVM, rbenv, and chruby — are not all compatible with one another. In my script, which prefers chruby, I check to see if RVM or rbenv are installed, and if so, I display a message to the user to uninstall them first, with a link to the uninstallation instructions.

Why did I pick chruby?

When I first started learning Ruby about 10 years ago, I used RVM because it was the most popular back then, and I stuck with it for several years. Once I wrote the 18F/laptop and monfresh/laptop scripts, I kept running into issues with RVM, so I switched to chruby and haven't looked back. For more technical details for my chruby preference, check out the README for my monfresh/laptop script.

I should mention there is another interesting version manager called asdf that not only supports Ruby, but other languages as well. I remember trying it a while back and running into an issue, but I might it give it another try and update this post accordingly.

The biggest reason of all to provide a script, or at a minimum Homebrew + Ruby manager documentation, is that Apple will no longer be preinstalling Ruby on macOS3. It's not yet clear to me when that change will happen, but I encourage you to stay ahead of the curve, like Homebrew did at the beginning of 2020 when they rewrote their installation script in bash instead of Ruby.

This concludes the Ruby manager option for installing gems and working with Ruby in general. It's the only option I recommend for long-term happiness, but I've included other options below for completeness, and to explain things like PATH, the difference between .zshrc and .bash_profile, and sudo.

Install a new version of Ruby with Homebrew

This is similar to the previous option, but instead of installing a Ruby manager with Homebrew, you can install Ruby directly with Homebrew. Doing this manually involves three commands, followed by relaunching the Terminal:

# 1. Install Homebrew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

# 2. Install the latest Ruby

brew install ruby

# 3. Update the `PATH` environment variable to point to Homebrew's Ruby
# installation. Replace .zshrc with .bash_profile if you're using bash.
# Keep reading for more details about this.

echo 'export PATH="/usr/local/opt/ruby/bin:$PATH"' >> ~/.zshrc

# 4. Relaunch your Terminal (or open a new tab)

To verify that you are using the Homebrew version of Ruby, run this command:

which ruby

You should see /usr/local/opt/ruby/bin/ruby.

If you don't, the two most likely reasons are that you didn't relaunch the Terminal, or the PATH is not set properly. A misconfigured PATH is a common source of confusion and errors. Instructions for setting the PATH are abundant on the internet, but very few explain what PATH does and why it needs to be updated.

PATH is an environment variable that contains a list of locations where the computer can find programs to run. The computer looks in each location in order of appearance until it finds the program (or not). You can view this list by running echo $PATH. Many programs that you might run from the command line on macOS, such as cd, whoami, or cal, live in /usr/bin. That's also where ruby, python and git are preinstalled on macOS. On a fresh macOS installation, the PATH already contains /usr/bin, which is why you can type ruby -v and get an answer on a brand new Mac.

When you install a new version of Ruby with Homebrew, it installs it in /usr/local/opt/ruby/bin. If you don't add this new location to the PATH, the computer won't know to look for it there when you type ruby, which is why you'll find many people complain that they're still getting the infamous permissions error, or that ruby -v is still showing the old macOS version.

The way to update the PATH is to run a command like this:

export PATH="/usr/local/opt/ruby/bin:$PATH"

export is the command used to set environment variables. The PATH variable expects a list of directories separated by a colon (:). Instead of writing all the locations one by one, we can append any new directories to the existing ones by adding :$PATH at the end. $PATH represents the current list of directories. Since we want macOS to use the Homebrew version of Ruby, we add that directory at the beginning so that the computer looks there first.

One thing to note is that setting the PATH is not required to use the Homebrew version of Ruby. You can always call programs by their full location. For example, if you type /usr/bin/ruby -v, it will always use the Ruby program in /usr/bin, and you should get ruby 2.6.3p62 on Catalina. Similarly, to switch to the Homebrew version, you can type /usr/local/opt/ruby/bin/ruby -v, and you should see ruby 2.7.2p137 or later. The point of setting the PATH is to be able to use the short program name without having to remember where it is located.

When people tell you to run echo 'export PATH="/usr/local/opt/ruby/bin:$PATH"' >> ~/.zshrc (as Homebrew suggests after installing Ruby), they're making it easy for you to add the right command to the right file. That echo command takes everything between the single quotes and adds it (>>) to a file called .zshrc in your user's home folder (~/). It's a shortcut that doesn't require you to open your text editor.

Why .zshrc? In Catalina, the default shell is the Z shell (zsh). In previous macOS versions, the default was Bash. Each shell supports a configuration file in your home folder that gets read every time you open a new terminal window (or tab). This allows the shell environment to be set up properly with your preferences, and so that the tools you depend on are ready to use. In zsh, the configuration file is .zshrc. In bash, it's .bash_profile.4 If you're not sure which shell you're using, you can find out by running echo $SHELL.

Now we can run gem install rails successfully. But wait! What happens if we try to use Rails? If you try rails -v for example, you'll get this error:

Rails is not currently installed on this system. To get the latest version,
simply type:

    $ sudo gem install rails

You can then rerun your "rails" command.

What do you mean it's not installed? I can see it when I run gem list! In some cases, this could be due to not relaunching the Terminal (or opening a new tab). In this case though, it's because Homebrew installs gems in a particular directory that needs to be added to the PATH. But how did macOS recognize the rails command to begin with? That's because there is a rails command in /usr/bin that Apple preinstalls for some reason. That message with the shocking suggestion to use sudo comes from Apple, not Rails.

Other gems that don't already exist in /usr/bin will also fail to be found after being installed, but with a different message:

$ gem install jekyll
$ jekyll -v

zsh: command not found: jekyll

If you paid attention when you ran brew install ruby, you might have noticed this message:

By default, binaries installed by gem will be placed into:
  /usr/local/lib/ruby/gems/2.7.0/bin

You may want to add this to your PATH.

In general, Homebrew's messages are worth reading and very helpful. This one could be clearer and more explicit. I will send them a pull request and see what they think.

To wrap up this section, in order to be able to install gems and use them with a Homebrew Ruby installation, your PATH needs to include the location of the Homebrew Ruby, as well as the gems location:

export PATH="/usr/local/opt/ruby/bin:/usr/local/lib/ruby/gems/2.7.0/bin:$PATH"

The 2.7.0 above assumes Homebrew installed a Ruby version that starts with 2.7. If you're using a different version (which you can check with ruby -v), replace 2.7 with the first two digits of your Ruby version.

The downside to installing Ruby with Homebrew is that you can only use one version of Ruby at a time. Once you start working on multiple projects, each with its own supported version of Ruby, this quickly becomes unsustainable.

Keep using the system Ruby by changing the installation path

While Apple doesn't grant you write access to the system Ruby directory, RubyGems does provide a way to specify an alternative location for gem installations. You can do that in one of two ways:

gem install rails --user-install
gem: --user-install

In addition, to use the gems, you need to update the PATH in your ~/.zshrc or ~/.bash_profile:

export GEM_HOME="$HOME/.gem"
export PATH="$HOME/.gem/ruby/2.6.0/bin:$PATH"

The 2.6.0 above assumes you are on Catalina. For previous macOS versions, replace 2.6 with the first two digits of your system Ruby, which you can find by running ruby -v.

While this option will allow you to run gem install without getting permission errors, it won't guarantee that all gems will be able to install successfully. A typical error in this scenario looks something like this:

ERROR: Failed to build gem native extension

That's because some gems require certain developer tools that don't come preinstalled on macOS. Apple provides these tools as part of the Xcode app, but also as a standalone package called the Command Line Tools (CLT). The most recommended way to install the CLT is with this command:

xcode-select --install

Unfortunately, this doesn't always work. Sometimes, macOS displays this error:

Can't install the software because it is not currently available from the
Software Update server.

What does seem to work consistently is to install Homebrew, which detects whether or not the CLT are installed, and if not, it uses a different and more reliable command to fetch and install them.

Once Homebrew is installed, you should be able to install gems using the system Ruby. The downside to this option is similar to the one above in that you can only use one version of Ruby, but it's even worse because you are stuck with the version that came with macOS, and you can't upgrade it until a new major version of macOS comes out, once a year on average. But as mentioned above, Apple is no longer planning on preinstalling Ruby in future versions of macOS. As of today, the latest version you could possibly get from Apple is 2.6.3 via Catalina, while the current latest stable release is 2.7.2.

Keep using the system Ruby by overriding Apple's protection

This involves elevating your privileges by using sudo, such as:

sudo gem install jekyll

Unfortunately, this is a common solution offered on the internet, either out of laziness, or unfamiliarity with better options. I advise you to stay away from it for several reasons:

In addition, prefixing the gem install command with sudo on its own will not install all gems successfully on a Mac that doesn't have the command line tools installed.

It's never as simple as it seems, which is why I advocate for a proven and reliable script that can set everything up.


  1. Virtual machines, containers, and cloud services are other options, but in this post, I'm only focusing on installing directly on macOS. 

  2. I am not affiliated with Discourse. I came across their installation instructions during my research for this post. 

  3. Look for the "Scripting Language Runtimes" section. 

  4. Bash also has .bashrc, but it doesn't get read automatically on macOS.