Tips and tricks for developing Rails applications on Apple Silicon


At Discourse we have been experimenting recently with getting our whole stack running natively on Apple’s new M1 machines. In the last few weeks things have come together and there are now a few of us using this setup for day-to-day development, with great results!

The vast majority of credit here goes to the Homebrew and Ruby teams - most stuff now ‘just works’ :heart_eyes:. The only changes we had to make at Discourse were to a few of our gems which had bundled binaries.

To summarise, in case anyone else would like to try it out:

  1. Install Homebrew using their default configuration. If you already installed Homebrew under Rosetta, you’ll need to uninstall and reinstall the arm64 version

  2. brew install redis, brew install postgres

  3. brew services start redis, brew services start postgres

  4. brew install ruby@2.7

    (follow the provided instructions to add it to your PATH, and restart the shell)

  5. ruby --version should now show the architecture as arm64-darwin20

  6. git clone https://github.com/discourse/discourse

  7. Update mini_racer line in Discourse’s Gemfile to point to this experimental branch

    gem 'mini_racer', github: 'rubyjs/mini_racer', branch: 'refs/pull/186/head'
    
  8. bundle install

  9. bin/rake db:create db:migrate

  10. bin/rails s

  11. Visit localhost:3000 :tada:

I’m sure that general idea will apply to most Rails apps.

In terms of performance, developing on M1 machines feels a lot faster than previous Intel MacBooks. We have some benchmarks which we use to compare development performance for Discourse. In most of the single-core CPU-bound tests, the M1 sits right up amongst latest-gen-cpu Linux desktops.

For multi-core workloads it’s respectable, although not quite matching those high-end desktops. Unfortunately for filesystem-based tasks we still seem to be plagued by the issues described in this topic, but the other performance gains do help to mitigate that.

As for ‘tips and tricks’, here are a few things that have been helpful to me:

  • If you’re ever unsure whether things are running natively, or under Rosetta, you can check the ‘Architecture’ column in Activity Monitor:

  • If you test/deploy your Rails applications in Docker, then they have a tech preview for M1 which has worked great for me. The only gotcha is that it will default to arm64 architecture, which might not be what you want, especially if you’re building an image to be deployed in production.

    This default can be changed using the --platform flag on a docker command, or via an environment variable DOCKER_DEFAULT_PLATFORM=linux/amd64

I’d be interested to know how many other people are developing Rails apps on M1. Have you seen performance gains? Any tips / tricks / gotchas to add?

10 Likes

Which editor(s) are you using?

1 Like

I’ve been using the VSCode Insiders Build, which has ARM64 support.

3 Likes

I’ve also been using M1 for about a month. I’m quite impressed how almost everything is already working. Rbenv’s build requires minor tweaks to compile.

A few caveats I found was that if you’re using the regular VS Code (not the insiders version), it’s better to run your system tests from macOS’ Terminal, rather than the VS Code Terminal or the Test Explorer UI. The reason is that VS Code doesn’t run natively and as a consequence macOS would spawn each sub process as Intel, even if it provides native binaries - i.e. using Firefox in system tests would start the Intel version which takes 20 seconds to start.

A weird issue I’ve seen is that sometimes my tests would run twice as slow, but more often than not - as fast or faster than on a 2018 Intel MacBook Pro.

4 Likes

In the largest Rails app I’m working on I just had to update the libfii gem as I was getting a strange error:

- ffi (1.13.1)
+ ffi (1.14.2)

I also installed a second version of Homebrew using iTerm running in Rosetta mode and then doing a git clone inside /usr/local, and finally aliasing to ibrew:

❯ which ibrew ibrew: aliased to arch -x86_64 /usr/local/Homebrew/bin/brew

so that I could install formulae such as mosh and terraform, which don’t yet natively install through the “ARM” homebrew. I had to add the path to these homebrew binaries after the path to the “ARM” binaries so they have the ARM ones have the chance to be picked up first.

Mostly everything else: browsers, redis, postgres, ruby, node; run natively… it’s incredible how smooth it’s been.

My only problem now is Docker. The technical preview can “build” the containers in amd64, but there’s no DNS resolution between them as far as I could tell, so I moved temporarily to running docker in a VPS.

2 Likes