DEV Community

Jervi
Jervi

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 (1)

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.