Running Node.js natively on Apple Silicon
Tags: applem1, performance, node, javascript
Published: 2021-01-14 21:35:55

---
title: Running Node.js natively on Apple Silicon
published: true
description: MacBook Air M1 feels much faster than my previous machine running most of the software through Rosetta 2 emulation mode. But how much faster it actually is when running things natively? 
tags: AppleM1, Performance, Node, JavaScript
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/s56nwczstd6o0j77pr1u.jpg
canonical_url: https://www.component-driven.dev/articles/native-node-on-m1
---

I just got the one of the newest M1-based MacBooks and I wanted to get it up and running as fast as possible so I went with the Migration Assistant and migrated everything from my old MacBook Pro 2017 running macOS 10. To my surprise it worked without *any* issues at all (I would expect at least it requires same OS versions).

Although it already felt much faster than my previous machine even running most of the software through Rosetta 2 emulation mode, I was still curious: how much faster it actually is when running things natively. 

Since the existing Homebrew installation wouldn't allow my updating or installing packages anymore because of more restrictive OS permissions, I was faced with "update the x86 homebrew" or “use an experimental cutting-edge ARM build”. I went with the latter and this post summarizes my experience.

## Step 0. Remove x86 zsh binary

One of the biggest confusion for me was the fact my shell would not run in arm64 mode. This was caused by the fact I already have been using zsh shell before and it was migrated over. The new macOS Big Sur comes with zsh as a default shell so you don’t need to install it separately anymore.

[https://twitter.com/okonetchnikov/status/1343140569430822914](https://twitter.com/okonetchnikov/status/1343140569430822914)

To solve this issue I had to remove x86 compiled zsh that I installed via Homebrew. This cannot be done with `brew remove` since Homebrew won’t have enough permissions but you can remove zsh manually:

```bash
// 1. verify you're not running native zsh

which zsh

// 2. if it is different from /bin/zsh, run

rm "$(which zsh)"
```

Verify by opening a new terminal session and running `arch`. 

```bash
➜ arch
arm64
```

## Step 1. Install Apple Silicon version of the Homebrew

To install the ARM version of Homebrew on the Apple Silicon Mac, I used the manual "untar anywhere" method: [https://docs.brew.sh/Installation#untar-anywhere](https://docs.brew.sh/Installation#untar-anywhere)

Starting from version 2.7.1, Homebrew supports Apple Silicon out-of-the-box.

This method allows installing Homebrew alongside with the x86 version which is probably a good idea for a time being since not all packages are yet pre-built for ARM. I had a few issues while building from sources, too. 

After installing Homebrew into separate directory, add the following to your `.zshrc` file 

```bash
# Support for two Homebrew installations
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
alias ibrew='arch -x86_64 /usr/local/bin/brew'
```

After that you can use both arm64 and x86 installations of Homebrew side-by-side.

For more information on Homebrew compatibility on Apple Silicon check out [https://github.com/mikelxc/Workarounds-for-ARM-mac](https://github.com/mikelxc/Workarounds-for-ARM-mac) and [https://soffes.blog/homebrew-on-apple-silicon](https://soffes.blog/homebrew-on-apple-silicon)

## Step 2. Install node and yarn

After you have installed Homebrew, install node (which includes npm) normally running  `brew install node` Install Yarn using `brew install yarn`.

To verify that you're running the Node in ARM architecture, enable the row "Architecture" in Activity Monitor,

![Screenshot of Activity Monitor](https://dev-to-uploads.s3.amazonaws.com/i/wcn7v36xmy8hh3ygh8zh.png)

Enable "Architecture" column with the right-click on the header. If node processes say "Apple" you're running them natively!

## Step 3. Install packages to run Gatsby

After running Node natively I had issues running Gatsby project. While doing `npm install` in the Gatsby project, it will fail because of the binding for the Sharp — a native C library Gatsby using — needs to be compiled under the ARM architecture. It didn't work out of the box for me since some native packages were missing. I was able to resolve it following this GitHub issue [https://github.com/lovell/sharp/issues/2460](https://github.com/lovell/sharp/issues/2460) and doing

```bash
cd ~/gatsby-project
brew install vips
rm -rf node_modules
npm install
```

After that the installation went well and I could run the project. There were no issues whatsoever running other JavaScript projects (using TypeScript or Bable).

## So, how much faster is ARM Node compared to x86

I ran a few tests on the same machine and also compared build speeds with my previous MacBook Pro 2017 that has 2,9 GHz i7 Quad Core CPU and 16 GB of RAM.

![Building the Next.js for https://www.monolisa.dev takes 10 seconds in arm64 Node](https://dev-to-uploads.s3.amazonaws.com/i/gjx10z87qbk3k6t5jlzq.jpg)

Building the Next.js for [https://www.monolisa.dev](https://www.monolisa.dev) takes 10 seconds in arm64 Node

![vs. 17 seconds when using Rosetta2 emulation and x86 Node](https://dev-to-uploads.s3.amazonaws.com/i/d94t8p7wmqo37gttxqm4.jpg)

vs. 17 seconds when using Rosetta2 emulation and x86 Node.

The results were... astonishing! 

> Running x86 vs arm64 node resulted in a ~40% improvement. Building a  Gatsby site with a few dozens of pages went from 55 seconds on MacBook Pro to 15 seconds on MacBook Air!

Let's not forget, MacBook Air is a fan-less machine that you can put on your lap without a risk of getting a skin burn. And my old computer was making noises similar to an airplane taking off 🤦‍♂️.

Here is the related Twitter thread: [https://twitter.com/okonetchnikov/status/1346162117506985985](https://twitter.com/okonetchnikov/status/1346162117506985985)

## Next steps

I still didn't finish this, but I'd like to try cleaning up previous installation of Homebrew completely and move everything to `/opt/homebrew` and also try installing only native packages. It didn't work for me for [fnm](https://github.com/Schniz/fnm) that I'm using to manage node versions meaning if I install a different node version over it it will use x86 build again.

## Automating installation

In order to save time in the future, I have a collection of scripts that automate the process of setting up a new computer. You can find them onGitHub [https://github.com/okonet/dotfiles](https://github.com/okonet/dotfiles). I've updated it accordingly so it should work on M1 Macs. Use it your own risk and please report issues and send PRs if you decide to use those scripts.