DEV Community

Jervi
Jervi Subscriber

Posted on

Setup Expo Build Environment on WSL2 (Without Android Studio nor Paying Expo Credits)

🔍 Why Even Build Locally?

1️⃣ Expo Cloud Builds cost money

Expo gives a small free quota. After that, you pay per build — which is fine for production, but not for rapid iteration or testing.

Local builds = unlimited free builds.


2️⃣ Android Studio is huge and unnecessary

A full Android Studio install includes:

  • IDE
  • Emulators
  • GUI tools
  • Extras you don’t need for CI-style builds

You only need the command-line SDK, build-tools, platform-tools, and NDK.

My setup installs only what is required, trimming the install from 30 GB → ~3 GB.


🛠️ Full Setup Guide (WSL2 Ubuntu 24.04)

✔ Works for Expo, React Native CLI, EAS Build
✔ Matches Expo’s cloud build environment
✔ No Android Studio required


1) Base Dependencies

sudo apt update
sudo apt install -y build-essential git unzip zip curl wget ca-certificates openjdk-17-jdk
Enter fullscreen mode Exit fullscreen mode

2) Node 20.19.4 (matches Expo CI)

Expo’s cloud logs showed Node 20.19.4, so we install that with NVM:

export NVM_DIR="$HOME/.nvm"
if [ ! -d "$NVM_DIR" ]; then
  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
fi
source "$NVM_DIR/nvm.sh"

nvm install 20.19.4
nvm alias default 20.19.4
Enter fullscreen mode Exit fullscreen mode

Optional package managers:

npm i -g yarn@1.22.22 pnpm@10.14.0 bun@1.2.20
Enter fullscreen mode Exit fullscreen mode

3) Android SDK + Build-tools + NDK r27b (minimal install)

No Android Studio. Only the components required for building APK/AAB.

Download command-line tools:

export ANDROID_HOME="$HOME/Android/Sdk"
mkdir -p "$ANDROID_HOME"

cd /tmp
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O cmdtools.zip
mkdir -p "$ANDROID_HOME/cmdline-tools"
unzip -q cmdtools.zip -d "$ANDROID_HOME/cmdline-tools"
mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/tools"
Enter fullscreen mode Exit fullscreen mode

Add to PATH:

export PATH="$ANDROID_HOME/cmdline-tools/tools/bin:$ANDROID_HOME/platform-tools:$PATH"
Enter fullscreen mode Exit fullscreen mode

Accept licenses:

yes | sdkmanager --licenses
Enter fullscreen mode Exit fullscreen mode

Install core packages:

sdkmanager "platform-tools" \
  "platforms;android-35" \
  "build-tools;35.0.0" \
  "build-tools;29.0.3" \
  "ndk;27.1.12297006" \
  "extras;google;m2repository" \
  "extras;android;m2repository"
Enter fullscreen mode Exit fullscreen mode

Set NDK env:

export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.1.12297006"
Enter fullscreen mode Exit fullscreen mode

4) Java + Gradle config

Expo uses Java 17 and specific Gradle JVM args:

export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"

export GRADLE_OPTS='-Dorg.gradle.jvmargs="-XX:MaxMetaspaceSize=1g -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" -Dorg.gradle.parallel=true -Dorg.gradle.daemon=false'
Enter fullscreen mode Exit fullscreen mode

5) Expo / EAS CLI + Tools

npm i -g expo-cli eas-cli
Enter fullscreen mode Exit fullscreen mode

Optional but useful:

Maestro:

curl -Ls "https://get.maestro.mobile.dev" | bash
Enter fullscreen mode Exit fullscreen mode

Bundletool:

sudo mkdir -p /opt/bundletool
sudo wget -q https://github.com/google/bundletool/releases/download/1.17.2/bundletool-all-1.17.2.jar -O /opt/bundletool/bundletool.jar
echo 'alias bundletool="java -jar /opt/bundletool/bundletool.jar"' >> ~/.bashrc
Enter fullscreen mode Exit fullscreen mode

6) Add Environment Variables Permanently

Append to ~/.bashrc:

# Android SDK
export ANDROID_HOME="$HOME/Android/Sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.1.12297006"

export PATH="$HOME/.nvm/versions/node/v20.19.4/bin:/opt/bundletool:$ANDROID_HOME/build-tools/29.0.3:$ANDROID_HOME/build-tools/35.0.0:$ANDROID_NDK_HOME:$ANDROID_HOME/cmdline-tools/tools/bin:$ANDROID_HOME/platform-tools:$PATH"

# Java
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"

# Expo Token
export EXPO_TOKEN="<your-expo-token>"

# Gradle
export GRADLE_OPTS='-Dorg.gradle.jvmargs="-XX:MaxMetaspaceSize=1g -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" -Dorg.gradle.parallel=true -Dorg.gradle.daemon=false'

# CI-like behavior
export CI=1
Enter fullscreen mode Exit fullscreen mode

Apply:

source ~/.bashrc
Enter fullscreen mode Exit fullscreen mode

🧪 7) Verify Everything

node -v
npm -v
java -version
sdkmanager --list | head -n 50
bundletool --version
maestro --version
Enter fullscreen mode Exit fullscreen mode

🚀 8) Do a Local EAS Build

Inside your Expo project:

npx eas build --local --platform android --profile production
Enter fullscreen mode Exit fullscreen mode

Now your build runs without consuming Expo credits, and behaves exactly like their cloud runner.


🎯 Final Thoughts

This approach gives you:

  • Zero-cost unlimited builds
  • A lightweight Android SDK install (no Studio needed)
  • A reproducible environment across machines
  • Perfect alignment with Expo CI logs

I built this by literally checking Expo’s cloud logs line-by-line, copying the exact versions, and replicating their environment on my local machine.

If you want, I can also generate:

✅ a one-click install script for all of this
✅ a Docker version of the environment
✅ the macOS or Windows-native variant

Just tell me!

This post is edited with AI for clarity and grammar.

Top comments (2)

Collapse
 
offsetexchange profile image
Offset Exchange •

This is great, and the "stop paying Expo credits" instinct is exactly right. One thing worth adding for anyone copying this: the part that bites after the build is the submission — getting the .aab to Play without eating a "versionCode already used" rejection when you're iterating fast. A local Gradle build gets you the .aab, but you still hand-upload or wire up Fastlane for the rest.

That last mile is actually why I ended up packaging this into a tool (OffsetBuild) — same local build, but it also submits straight to Play and auto-bumps the versionCode by checking Play first. It's paid, full disclosure, but there's a free --demo that runs the whole flow. For folks who love wiring their own pipeline this guide is the way; for anyone who'd rather not maintain it, that's the shortcut.

Collapse
 
sherrydays profile image
Sherry Day •

This is fantastic—“Expo CI cosplay” on WSL2 without paying Expo tax or installing the Android Studio mothership. Love the 30GB → 3GB diet. My SSD just breathed a sigh of relief.