EAS Build & Submit: Ship React Native Without Local Chaos
Expo Application Services (EAS) replace the worst part of shipping React Native apps.

EAS Build & Submit: Ship React Native Without Local Chaos
Expo Application Services (EAS) turn app‑store submissions from a multi‑day manual chore into a single CLI command. No more “works on my machine” debacles, no more hunting for the right provisioning profile on a colleague’s laptop. This walkthrough covers the exact EAS setup—build profiles, credentials, secrets, OTA rollouts, and CI pipelines—that a React Native team needs to ship fast and never touch a local signing nightmare again.
What Expo Application Services (EAS) Actually Replaces
EAS is the umbrella for three hosted services that take over the worst parts of mobile delivery: EAS Build, EAS Submit, and EAS Update. EAS Build gives you cloud‑hosted build workers that compile your app and automatically handle code signing for both iOS and Android. EAS Submit then pushes the resulting binaries directly to the App Store Connect and Google Play Console. Finally, EAS Update lets you push JavaScript and asset changes over the air, sidestepping a full rebuild for quick bug fixes.
The moment you stop running expo build:android on a random developer’s machine and move to the managed service, you eliminate the broken CI runner that can’t find the right Android SDK, the stale Apple Development certificate that just expired, and the “I deleted the keystore” panic. The Expo team designed EAS Update to be a drop‑in replacement for expo publish, while the submit workflow automates what used to be a manual upload inside a browser.
Build Profiles That Match Your Workflow
Every build in EAS runs against a build profile defined in your eas.json. Profiles let you separate development, preview, and production concerns without duplicating configuration. A typical setup looks like this:
{
"cli": { "version": ">= 3.0.0" },
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"ios": { "resourceClass": "m1" }
},
"production": {
"distribution": "store",
"autoIncrement": true,
"env": {
"APP_ENV": "production"
}
}
}
}- Development: Uses the Expo Dev Client, letting you load any JS bundle and iterate with Fast Refresh. Distributed internally so testers can install without device registration hoops.
- Preview: A release‑like build with ad‑hoc provisioning, perfect for a staging version you share with product managers. Use it to catch performance regressions before the store submission.
- Production: Ships to the App Store or Play Store. Here you enable auto‑increment, strip debug code, and inject production‑only environment variables.
Once you’ve tuned your production profile, you’ll also want to shrink that APK—see why your React Native app is so big and how to trim it.
Credentials and Signing Without the Headaches
Manually managing iOS certificates and provisioning profiles is a rite of passage nobody wants. EAS can take over entirely: run eas credentials once and let the service generate your distribution certificates, push notification keys, and App Store profiles. On Android, all you need to do is supply a keystore—or let EAS generate a new one and store it securely in the Expo servers. The command eas build --platform ios will prompt you only on the first run, then reuse those credentials forever.
For sensitive enterprise setups, you can switch from managed to local credentials with a single field in eas.json ("credentialsSource": "local"). This drops the certificate files on your machine, giving you full ownership while still using the same build workers. The EAS Submit automation picks up those signed binaries and uploads them without you ever opening Apple Transport or the Play Console.
Environment Variables and Secrets
Shipping the same binary to staging and production with different API endpoints is a classic source of outages. EAS handles this cleanly through build‑time environment variables defined in your profile and via the EAS CLI.
# Set a secret that never hits your codebase
eas secret:create --name API_KEY --value "sk‑live‑..."In eas.json, reference secrets like "$API_KEY" inside the "env" block of any profile. For variables that don’t need secrecy, just drop a .env file and EAS will pick it up. Crucially, these values are embedded at build time, so they survive OTA updates—your app always knows which backend to hit without a fragile runtime switch.
Over‑the‑Air Updates with EAS Update
Once native shell is in the store, bug fixes and content tweaks shouldn’t wait for a review cycle. EAS Update pushes only your JavaScript bundle and assets, making it possible to deploy in minutes. The magic is the relationship between runtime version and channel. You define a runtime version in app.json (e.g., "runtimeVersion": "1.0.0") and, after a build, point an update channel to that version:
// app.json snippet
{
"expo": {
"updates": {
"url": "https://u.expo.dev/your-project-id",
"runtimeVersion": {
"policy": "sdkVersion"
}
}
}
}When you publish:
eas update --branch production --message "Fix crash on checkout"Only devices already running the production branch of the matching runtime version will see the update. You can stage rollouts by pushing to a staging branch first, letting testers validate, then merging to production with eas channel:rollout. The step‑by‑step workflow shows exactly how to combine channels with fast rollbacks. Remember: OTA covers JavaScript and assets only; any native change—new permission, a native module—still demands a full build.
Integrating EAS into CI and Keeping Releases Fast
Locking your releases behind a single developer’s laptop is a recipe for delay. EAS CLI was designed to run non‑interactively, so you can trigger builds from GitHub Actions, GitLab CI, or any job that can provide a EXPO_TOKEN.
A minimal GitHub Actions workflow:
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: EAS Build (production)
run: npx eas build --platform ios --profile production --non-interactive --json
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}EAS Build pricing is based on build minutes; the free tier gives you enough for a few builds before you pay per minute. Once that cap hits, builds simply fail, which can be disastrous mid‑sprint. That’s why many teams prefer to let their CI machine run eas build --local, which compiles the app on their own hardware without touching the cloud queue and without consuming free minutes. Local builds are also the right choice when you need absolute control over the build environment—think custom CocoaPods plugins or sensitive on‑device keys that can’t leave your network.
If you need help wiring this into your team’s workflow, our mobile services cover everything from CI to release management.
When to Build Locally Instead of Using EAS
Sometimes throwing money at build minutes isn’t the answer, and the EAS worker abstraction gets in the way. Drop back to a local build when:
- You’re hammering out native‑code bugs and need a build‑run‑crash cycle measured in seconds, not minutes.
- Your company policy forbids sending signing certificates to a third‑party service (use local credentials).
- You’re offline or behind a firewall that blocks the remote build service.
- You’ve already exhausted the free minutes and don’t want to wait for the next billing cycle.
The command eas build --platform android --local fires up the same Docker‑based pipeline on your machine and outputs the APK/AAB directly. You get the exact same signing automation without a remote build. For rapid development, npx expo run:ios with a development client gives you a native debug build in seconds—and that’s often faster than waiting for the cloud queue.
FAQ
Can I use EAS Build for free?
Yes. The free tier includes a limited number of build minutes per month—enough for a few builds if you’re careful. Once you exceed the free minutes you either pay as you go or switch to local builds (eas build --local), which don’t consume any plan minutes and still produce signed binaries.
Do EAS Update over‑the‑air changes bypass app‑store review?
No. EAS Update pushes only your JavaScript bundle and assets, so it does not go through the app‑store review process. However, you must still comply with each store’s guidelines; OTA updates are ideal for quick bug fixes and content changes, but any native‑code addition—like new permissions or a new native module—requires a full build and resubmission.