local-expo-build
v1.1.3
Published
One-stop CLI for local Expo builds. Android (stable) → AAB/APK with EAS-synced versionCode and managed signing. iOS (experimental, macOS) → IPA via xcodebuild with auto export-options.plist.
Maintainers
Readme
local-expo-build
Build signed Expo Android apps on your own machine. Windows, macOS, and Linux supported.
local-expo-build turns an Expo project into a signed Android APK or AAB without waiting for an EAS cloud build. It handles Expo prebuild, Gradle compatibility, versioning, release signing, and artifact discovery in one repeatable workflow.
Android is stable. Local iOS IPA builds are experimental and require macOS.
Why use it?
- Build locally on Windows, macOS, or Linux.
- Produce a signed AAB for Google Play or a signed APK for direct installation.
- Keep control of your keystore and build scripts.
- Use EAS when you want it, without requiring an EAS cloud build for the local pipeline.
- Run the same workflow from a terminal, a local browser UI, or scripts committed to your project.
Quick start
From the root of an Expo project:
npx local-expo-build initThe setup wizard checks your environment, helps configure the Android package and release keystore, and adds two npm scripts to your project. Then build the artifact you need:
# Google Play upload
npm run build:android:aab
# Direct device installation or testing
npm run build:android:apkThe finished artifact is written under android/app/build/outputs/ and the CLI prints its absolute path and size when the build completes.
One-off build without scaffolding
Use runner mode when you do not want init to add files or scripts to your project:
npx local-expo-build doctor
npx local-expo-build build android --aabUse --apk instead of --aab for an APK.
Install globally
npx is enough for most projects. To keep the command available globally:
npm install --global local-expo-build
local-expo-build initWhat the build does
For a release Android build, the CLI normally:
- Checks the local toolchain and Expo configuration.
- Runs Expo prebuild and applies the Gradle version required by the detected Expo SDK.
- Bumps the app version and, when available, gets the next Android versionCode from EAS.
- Restores or configures the release keystore and injects the Gradle signing configuration.
- Runs assembleRelease for an APK or bundleRelease for an AAB.
- Optionally syncs the new versionCode back to EAS.
- Prints the final artifact path and size.
The pipeline is designed to recover the keystore after expo prebuild --clean, which can otherwise remove files under android/.
Choose a workflow
Scaffold mode — recommended for repeated builds
npx local-expo-build init
npm run build:android:aabinit adds an editable scripts/ build pipeline and these entries to package.json:
{
"scripts": {
"build:android:apk": "node scripts/build.js apk",
"build:android:aab": "node scripts/build.js aab"
}
}The generated scripts are yours to review and customize. Refresh them later with:
npx local-expo-build update-scriptsRunner mode — no project files added
npx local-expo-build build android --aabRunner mode is useful for one-off builds or when you prefer not to commit the generated pipeline.
Browser UI
npx local-expo-build uiThe UI runs on 127.0.0.1 and provides:
- Environment Doctor checks and guided fixes.
- Android APK/AAB builds with live logs.
- Keystore import, generation, EAS fetch, and rehydration.
- EAS project linking and eas.json setup when needed.
- A scaffold action equivalent to init.
Useful options:
npx local-expo-build ui --port 3847 --no-open --logs--logs mirrors safe server and build output to the terminal. Credential-like values and large base64 values are redacted.
UI screenshots
The UI guides you from setup through a completed release build:






Requirements
All platforms
- Node.js 20 or newer.
- An Expo project with dependencies installed: npm install, pnpm install, or yarn.
Android builds
- JDK 17 is recommended for Expo SDK 55.
- Android SDK with ANDROID_HOME configured.
- keytool on PATH, included with the JDK.
- eas-cli is optional. It is only needed for EAS credential fetch, EAS project setup, or version-code synchronization.
Run this before your first build if you are unsure what is missing:
npx local-expo-build doctoriOS builds
- macOS only.
- Xcode 14 or newer with Command Line Tools.
- An Apple Developer account for distribution signing.
- A distribution certificate (.p12) and provisioning profile.
Android signing
The first release build checks for a release keystore. If one is missing, the interactive setup can:
- Rehydrate an existing EAS credentials.json and .jks.
- Register an existing .jks file.
- Generate a new keystore with keytool.
- Fetch the project keystore through EAS.
Start the picker directly with:
npx local-expo-build keystore setupIndividual operations are also available:
npx local-expo-build keystore import # use an existing .jks
npx local-expo-build keystore create # generate a new keystore
npx local-expo-build keystore fetch # download through EAS
npx local-expo-build keystore rehydrate # restore from credentials.jsonIf credentials.json already references a downloaded keystore, rehydrate recreates keystore.properties and copies the keystore into android/app/ without asking for the passwords again.
Keep signing files private
keystore.properties and credentials.json contain plaintext keystore passwords. The setup flow adds them, along with keystore files, to .gitignore. Never commit or share them. Back up the keystore and its passwords securely: losing the original signing key can prevent updates to an app already published on Google Play.
Command reference
Setup and diagnostics
local-expo-build Run init inside an Expo project
local-expo-build init [options] Scaffold scripts and run setup
local-expo-build doctor Check and optionally fix the environment
local-expo-build ui [options] Launch the local browser UI
local-expo-build update-scripts [options] Refresh scaffolded scriptsinit options:
--force Overwrite existing scaffolded scripts
--no-doctor Skip pre-flight checks
--no-keystore Skip interactive keystore setup
--no-build Skip the post-setup build promptAndroid
local-expo-build build android --aab
local-expo-build build android --apkCommon options:
--aab Build a release AAB (default)
--apk Build a release APK
--profile <name> EAS profile used for version-code lookup (default: production)
--clean Run expo prebuild --clean
--no-clean Skip the clean prebuild prompt
--no-bump Skip version bumping
--no-sync Do not sync versionCode back to EAS
--no-prebuild Reuse the existing native project
--debug Build a debug APK without release signing or EAS
--max-ram <ram> Set Gradle/Node memory, for example 4g or 8gFor CI or other non-interactive environments, use explicit flags and skip setup prompts when appropriate:
npx local-expo-build init --no-doctor --no-keystore --no-build
npx local-expo-build --dry-run build android --aab--dry-run shows the planned pipeline without running the build or writing project files.

iOS (experimental)
local-expo-build build ios --method app-store \
--team-id ABCDE12345 \
--bundle-id com.example.app \
--profile-name "Your Distribution Profile"Supported methods are app-store, ad-hoc, development, and enterprise. The scheme is detected automatically for the standard Expo prebuild output; use --scheme when needed.
The iOS pipeline runs Expo prebuild, xcodebuild archive, and xcodebuild -exportArchive, producing an IPA under ios/build/export/. You must install the .p12 in Keychain Access and the provisioning profile in Xcode/macOS yourself. Uploading to TestFlight or the App Store is not included.
Global options
These options can be used with the commands above:
--cwd <path> Run against a different Expo project directory
--verbose Print additional diagnostic output
--dry-run Preview actions without making changes
--no-update-check Skip npm and scaffold update checks
--yes-update Apply an available CLI update without promptingRun npx local-expo-build --help or append --help to any command for the complete current help text.
EAS: optional, not required
Local builds do not require an EAS account, eas.json, or an EAS cloud build. EAS is useful when you want to:
- Store and fetch a team keystore.
- Link the project and generate eas.json.
- Allocate the next versionCode and sync it back after a build.
- Submit a locally produced artifact using the rest of your EAS workflow.
For a project that already has a keystore on EAS:
npx local-expo-build keystore fetch
npx local-expo-build keystore rehydrate
npx local-expo-build build android --aabIf EAS is unavailable, the local keystore providers still work. Version-code synchronization is skipped with a warning when it cannot run; use --no-sync when managing versions yourself.
Files created or updated
Depending on the workflow, the CLI may create or update:
app.json expo.android.package and EAS project link
eas.json EAS build profile configuration, when configured
keystore.properties Local Gradle signing configuration (private)
credentials.json EAS-compatible local credentials (private)
android/app/*.jks or *.p12 Release signing key (private)
scripts/ Scaffolded, editable build pipeline
android/app/build/outputs/ Generated APK/AAB artifactsThe CLI adds sensitive signing files to .gitignore during setup. Review the changes before committing, especially if your project uses a custom app.config.* instead of app.json; dynamic config is read for checks but is not rewritten automatically.
Troubleshooting
The Android package is missing
Add an Android application ID to app.json, or run doctor and accept its fix:
{
"expo": {
"android": {
"package": "com.example.myapp"
}
}
}The keystore file is missing after prebuild
Rehydrate or re-import the signing key, then update the scaffolded scripts if this is an older project:
npx local-expo-build keystore rehydrate
npx local-expo-build init --forceeas credentials fails
EAS credential commands need a linked project and usually eas.json. Run:
eas init
eas build:configure --platform android
npx local-expo-build keystore fetchOr use keystore create or keystore import to stay entirely local.
Google Play rejects the artifact
The most common causes are a reused versionCode or a different signing key. Avoid --no-sync unless you manage versioning yourself, and use the original release keystore for updates to an existing app.
Development
npm install
npm test
npm run buildTo test a local package in another Expo project:
npm run build
npm pack
npm install ../local-expo-build/local-expo-build-<version>.tgzContributions are welcome. See CONTRIBUTING.md, CHANGELOG.md, and LICENSE.
Not affiliated with Expo or Google. “Expo” and “EAS” are trademarks of 650 Industries, Inc. This project is independently maintained and uses EAS public APIs where selected features require them.
