Install Ruby on macOS Monterey (12.6.x) and Ventura (13.x)
Updated on
If you’ve recently updated your Intel or Apple Silicon Mac to macOS 12.6.x (Monterey) or 13.x (Ventura), or if you updated the Apple command line tools and/or Xcode to version 14 on macOS 12.5 or higher, and are unable to install Ruby or certain gems (most likely due to an error that says “ld: symbol(s) not found for architecture arm64” or “ld: symbol(s) not found for architecture x86_64”), this guide is for you.
Assuming you already have Homebrew installed, you can find out which version of the command line tools and/or Xcode you’re using by running brew config
, then look for the lines that start with CLT:
and Xcode:
. If the version starts with 14
, then keep reading.
UPDATE: PR 6440 in the Ruby GitHub repo fixes this issue in 3.1 and 2.7. This means that when 3.1.3 and 2.7.7 are released, you’ll be able to install them without any workarounds. I confirmed this on my M1 MacBook Air by cloning the Ruby GitHub repo, checking out the ruby_3_1
branch, and compiling Ruby 3.1.3.
UPDATE on 11/26/22: Ruby 3.1.3, 3.0.5, and 2.7.7 are now available, so you should be able to install them without any of the workarounds mentioned in this article. For older Ruby versions, you’ll still need the workarounds.
However, instead of using workarounds for older versions, I highly recommend you update your project to a newer version of Ruby. I’ve written a detailed guide that explains how and why to upgrade the Ruby version in your project.
TL;DR
If you’re a Ruby on Mac customer, check your email for a link to download the latest version. After that, you can update to the latest version by simply running romup
in your Terminal if you have the Prime version, or rom update
in the Ultimate version.
For non-customers who use ruby-install
or frum
, if you want to install versions of Ruby 3.1.x older than 3.1.3, or Ruby 2.7.x older than 2.7.7, you can install them by adding the --enable-shared
flag, like this:
ruby-install 3.1.2 -- --enable-shared
If you use a different tool, such as asdf
or rbenv
, you might be able to install these older versions (such as 2.7.6 or 3.1.2) out of the box because they both use ruby-build
, which adds the --enable-shared
flag by default.
As an aside, that flag was turned on by default in ruby-build back in 2019 because some gems, like rice
, failed to install otherwise. However, turning on that flag affects performance. Today, you can install rice
without the --enable-shared
flag (I tested with Ruby 3.1.3 on both my Intel and M1 Mac), so there doesn’t seem to be a good reason to turn it on by default anymore. Now that 3.1.3 and 2.7.7 are released, you should no longer need to add the --enable-shared
flag.
If you use Ruby on Mac, you don’t have to worry about any of this, now or in the future. It automatically determines whether or not any flag is needed, and optimizes the installation for you.
If you’re having issues running older versions of Ruby (2.6 and below), you have the following options:
- Update your apps to at least 3.1.4. Read my guide that shows how and why to upgrade the Ruby version in your project.
- Revert to version 13.4 of the command line tools (this only works on Monterey)
- Set up a separate Ruby dev environment with Rosetta.
- Buy Ruby on Mac Ultimate, which includes support for 2.6.10 on Apple Silicon in native mode (without Rosetta), even on macOS Ventura (13.x) and version 14.x of Xcode/command line tools. It also let you easily install Ruby 2.5.9 and older with Rosetta.
My troubleshooting journey
Like many people, I upgraded my Macs to macOS 12.6 yesterday. I have two Intel Macs, and one M1 MacBook Air. After the upgrade, all my existing Ruby projects were still working fine. Then I noticed an email from F5Bot with a new Reddit post matching the keyword “install ruby”.
In the post, Ed Mangimelli explains that he was having trouble installing Ruby 2.6.x after the update to 12.6, so I tried to reproduce the issue on all my Macs. Knowing that the oldest version of Ruby that can be installed natively (i.e. without Rosetta) on Apple Silicon is 2.6.8, I first tried to install 2.6.10 with ruby-install
, the Ruby installation tool I normally use.
Why 2.6.10? Because it’s the latest in the 2.6.x series, and it’s recommended to update your projects to the latest patch version to keep them secure and up to date.
Important side note
Ruby 2.6.x and 2.7.x reached end of life, so they should not be used in production for any critical projects. Instead, I recommend updating your Ruby project to at least 3.1.4. Read my guide that shows how and why to upgrade the Ruby version in your project.
Similarly, if your project is using a version of 2.6.x lower than 2.6.10, that doesn’t mean you have to use that version. I keep seeing a lot of people try to install Ruby 2.6.6 (or other older 2.6.x versions) on Apple Silicon, and recommendations to add the -Wno-error=implicit-function-declaration
flag.
While that may allow Ruby to be installed, it can lead to other issues down the line. It’s also dangerous because it overrides macOS defaults.
Benoit Daloze, one of the rbenv
and ruby-build
maintainers, explains in more detail why this flag should not be used:
First, it does not seem advisable to disable something the OS does by default. Actually it can be a pretty dangerous workaround, because then the compiler does not know precise argument types and could end up passing arguments incorrectly, which could cause all kinds of problems.
Also, recent CRuby enables that flag by default, and it would be very bad to disable something CRuby enables on purpose.
Back to the troubleshooting
Installing 2.6.10 with ruby-install
failed with this error on my M1 Air:
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [../../.ext/arm64-darwin21/strscan.bundle] Error 1
make[1]: *** [ext/strscan/all] Error 2
make: *** [build-ext] Error 2
!!! Compiling ruby 2.6.10 failed!
On my Intel MacBook Air, it worked fine, but it failed on my Intel iMac with this error:
linking shared-object openssl.bundle
ld: warning: -undefined dynamic_lookup may not work with chained fixups
installing default openssl libraries
extracting ripper.y from ../.././parse.y
compiling compiler ripper.y
ripper.y:762.9-16: syntax error, unexpected identifier, expecting string
make[2]: *** [ripper.c] Error 1
make[1]: *** [ext/ripper/all] Error 2
make: *** [build-ext] Error 2
!!! Compiling ruby 2.6.10 failed!
That didn’t make sense, so I ran through my usual troubleshooting steps. The first thing I check after a macOS update is whether Homebrew is happy:
brew doctor
It said “Your system is ready to brew,” so the next step was to reinstall all the Ruby-related tools so they can be compiled against the latest Apple command line tools that were installed as part of the update to 12.6. Since I use chruby
and ruby-install
with the fish shell, these are the tools I reinstalled:
brew reinstall automake bison gdbm libffi libyaml openssl@1.1 openssl@3 readline chruby ruby-install chruby-fish
Then I tried installing 2.6.10 again, and it worked this time on the Intel iMac. As of late November 2022, 2.6.10 now no longer works on Intel Macs with the latest version of the CLT.
Next, I tried installing 3.1.2 on my M1 Air with ruby-install
, but it failed with a different error:
linking shared-object -test-/arith_seq/extract.bundle
Undefined symbols for architecture arm64:
"_rb_arithmetic_sequence_extract", referenced from:
_arith_seq_s_extract in extract.o
"_rb_ary_new_capa", referenced from:
_arith_seq_s_extract in extract.o
"_rb_ary_store", referenced from:
_arith_seq_s_extract in extract.o
"_rb_define_singleton_method", referenced from:
_Init_extract in extract.o
"_rb_path2class", referenced from:
_Init_extract in extract.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [../../../../.ext/arm64-darwin21/-test-/arith_seq/extract.bundle] Error 1
make[1]: *** [ext/-test-/arith_seq/extract/all] Error 2
make: *** [build-ext] Error 2
The next step was to rule out the tool used for installing Ruby and see if I can compile Ruby manually:
brew install wget
cd ~/src
wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.2.tar.xz
tar -xzvf ruby-3.1.2.tar.xz
cd ruby-3.1.2
./configure --with-opt-dir="$(brew --prefix openssl@1.1):$(brew --prefix readline):$(brew --prefix libyaml):$(brew --prefix gdbm)" --prefix=/Users/moncef/.rubies/ruby-3.1.2
make
make install
It failed with the same error!
So then I try rbenv
, and it was able to install 3.1.2 just fine, but I notice that it’s using OpenSSL 3, so I try ruby-install
again with OpenSSL 3, but that still doesn’t work.
Then I compare the configuration options between ruby-install
and rbenv
and I notice that rbenv
has enable shared libs
turned ON, but ruby-install
has it turned OFF.
With a carefully crafted search query in DuckDuckGo, I found this Ruby bug report with macOS 13 Ventura Beta which was using Xcode 14 beta. macOS 12.6 uses the Xcode 14 command line tools too.
In the bug report, they provide a fix by passing in some configuration options, one of which was --enable-shared
. Aha! Just like rbenv
is doing. But when I tried to use both flags as written in the bug report, it didn’t work:
--with-out-ext=+,bigdecimal --enable-shared
So I thought maybe it’s supposed to be 2 separate flags for excluding the extensions:
--with-out-ext=+ --with-out-ext=bigdecimal
And that worked!
But then I thought is there really an extension called +
? So, I removed it and it still worked. So it looks like only bigdecimal needs to be excluded. But then I thought, isn’t that gonna cause problems with Rails app or other Ruby projects? And sure enough, my Rails apps didn’t work anymore because they were looking for bigdecimal.
This is another example of the dangers of blindly adding configuration options when installing Ruby. Just because Ruby installed successfully doesn’t mean everything else will be fine, and most people will not realize that the issues are due to the way Ruby was compiled.
So I took that flag out and just used the --enable-shared
flag, and that still worked! And my Rails apps still work as well.
So then I tried installing 2.6.10 and 2.6.9 with this flag:
ruby-install 2.6.9 -- --enable-shared
But they both failed with similar errors:
linking static-library libruby.2.6-static.a
linking shared-library libruby.2.6.dylib
Undefined symbols for architecture arm64:
"__mh_execute_header", referenced from:
_rb_dump_backtrace_with_lines in addr2line.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [libruby.2.6.dylib] Error 1
!!! Compiling ruby 2.6.9 failed!
Install Ruby with v13 of the command line tools
Note that this is not possible on macOS 13.x (Ventura).
So then I tried reverting to the previous command line tools, as Ed suggested in his Reddit post. The only difference is I’m only downloading the standalone command line tools, and not the full Xcode app.
Note that it should never be necessary to download the huge 7GB Xcode app unless you’re building macOS and iOS apps. In other words, if you’re never going to use the Xcode app itself, all you need are the standalone Command Line Tools.
- Download “Command Line Tools for Xcode 13.4”. I think you can sign in with your existing Apple ID without having to create a new developer account.
- Install them, but keep the DMG when it asks you if you want to Trash it, just in case you need to install them again. I also recommend downloading “Command Line Tools for Xcode 14” as well, so you can switch between both versions easily.
- In your terminal, run
xcode-select -p
. If it says/Library/Developer/CommandLineTools
, you’re good to go. Otherwise, change the location withsudo xcode-select -s /Library/Developer/CommandLineTools/
- Check the path again with
xcode-select -p
- Check that Homebrew is using the correct CLT with
brew config
. You should seeCLT: 13.4.0.0.1.1651278267
. If not, remove the CLT withsudo rm -rf /Library/Developer/CommandLineTools
and reinstall them. - Reinstall all Ruby-related tools with Homebrew. In my case, it’s these:
brew reinstall automake bison gdbm libffi libyaml openssl@1.1 openssl@3 readline chruby ruby-install
- Completely remove any existing Ruby versions from your machine, then install them again.
I was then able to install 2.6.9, but 2.6.10 still failed for some reason. This doesn’t affect me since I upgraded all of my Ruby projects to 2.7.7 or 3.1.3. I highly encourage you to do the same, especially since going from 2.6.x (and possibly older versions too) to 2.7.7 should be as easy as updating the version in all the files where you specify it.
To confirm, I looked at the Git history for a bunch of my projects, and whenever I updated Ruby 2.x versions, I rarely had to change a single line of app code. All I had to do was replace the previous version with the new one in all files where the Ruby version was specified.
However, going from 2.x to 3.1.3 will likely require code changes, but it’s worth it. It’s better to do it sooner rather than later because the more code you add while using 2.x, the more you might need to change when you update to 3.1.3.
Conclusion
After updating to macOS 12.6 or 13.x, you might still be able to install older 2.7.x and 3.x versions of Ruby with rbenv
or asdf
. With ruby-install
and frum
, you’ll need to add the --enable-shared
flag. Now that 2.7.7 and 3.1.3 are released, you should no longer need to add any flags.
If you’re having issues, check Homebrew’s health with brew doctor
, fix any issues it reports, and try reinstalling the Ruby tools.
If you’re having issues running older versions of Ruby (2.6 and below), you have the following options:
- Update your apps to at least 2.7.8.
- Revert to version 13.4 of the command line tools (this is not possible on macOS 13.x (Ventura))
- Set up a separate Ruby dev environment with Rosetta.
- Buy Ruby on Mac Ultimate, which includes support for 2.6.10, even on Ventura and version 14.x of Xcode/command line tools.