EAS Build Explained: Profiles, Credentials, and What It Costs
EAS Build explained for 2026: build profiles in eas.json, managed credentials, local builds, versioning, and what Expo's plans and per-build prices cost.
Full-Stack, Mobile & AI Engineer
Before Expo's cloud build service, shipping a React Native app meant owning a Mac build machine, keeping Xcode and Android tooling in sync, and guarding a folder of certificates that only one person understood. EAS Build, Expo's cloud service for compiling and signing iOS and Android binaries, replaces most of that with a configuration file and a command, and it works for bare React Native projects as well as Expo ones.
This guide explains how it works in practice: the build profiles in eas.json, how credentials are handled, how to keep version numbers straight, when to build locally, and what it actually costs in 2026.
Key takeaways
- Your iOS and Android apps compile in Expo's cloud, so you don't need a Mac to ship iOS.
- Profiles in
eas.json(usually development, preview and production) decide what kind of build you get and where it goes. - EAS can create and store your signing credentials: iOS certificates and provisioning profiles, and Android keystores.
- Use remote versioning with
autoIncrementso build numbers never collide. - Cost (October 2026): a Free plan with up to 15 iOS and 15 Android builds a month on a low-priority queue, a $19/month Starter plan with $45 of build credit, and per-build prices of $1 to $4 beyond the credit.
How a cloud build works
When you run eas build, the CLI uploads your project to Expo's build servers. A fresh virtual machine installs your dependencies, runs prebuild if you use Continuous Native Generation, compiles the native app with Xcode or Gradle, signs it with your credentials and gives you a downloadable artifact: an .ipa for iOS, an .aab or .apk for Android.
You don't need an Apple Developer account for iOS simulator builds (set "ios": { "simulator": true } in a profile); builds for real iPhones and the App Store do need one. Nothing about this locks you in. The output is a normal signed app, and you can always build the same project locally.
Build profiles in eas.json
Profiles are named recipes. A typical setup:
{
"cli": { "appVersionSource": "remote" },
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"channel": "preview"
},
"production": {
"autoIncrement": true,
"channel": "production"
}
},
"submit": { "production": {} }
}| Profile | Used for | Who installs it |
|---|---|---|
development | A development build with your native code and dev tools | Your team, while building features |
preview | A release-like build for testing | Testers and clients, via internal distribution |
production | The store build | App Store and Google Play |
The channel field connects each build to an EAS Update channel, so over-the-air updates reach the right builds.
Credentials: who holds the keys
On the first iOS build, EAS asks to sign in to your Apple account and can create the distribution certificate and provisioning profile for you. On Android, it can generate an upload keystore. Both are stored encrypted on Expo's servers and reused for every build.
You can also bring your own credentials with a credentials.json file, or manage them with eas credentials. Either way:
- Keep the Apple and Google accounts in your company's name.
- Download a backup of the Android upload keystore and store it somewhere safe.
- Enable Play App Signing, so a lost upload key can be reset.
Version numbers without the headaches
Every store upload needs a new build number (iOS) or versionCode (Android). With "appVersionSource": "remote" and "autoIncrement": true on the production profile, EAS stores and increments those numbers for you, so two developers building on the same day never upload a duplicate. You still set the user-facing version (like 1.4.0) yourself when a release deserves it.
Building locally
eas build --platform android --profile production --local--local runs the same build process on your own machine (iOS builds still need a Mac). It's useful for debugging a build failure, for strict environments where code can't leave your network, or when you've used up your cloud builds for the month.
Node, Xcode and environment variables
Each build profile can pin the toolchain, so a build behaves the same next month as it does today:
nodesets the Node.js version for the build, for example"node": "22.12.0".image(underiosorandroid) picks the build image, which determines the Xcode or Android tooling version."image": "latest"follows Expo's newest image; pin a specific image when you need a fixed Xcode version, and check Expo's build infrastructure page for the current list.resourceClasschoosesmediumorlargeworkers (large isn't available on the free plan).environment(development,previeworproduction) decides which EAS environment variables the build receives.
Environment variables live in EAS rather than in your repository:
eas env:set --name EXPO_PUBLIC_API_URL --value https://api.example.com --environment production --visibility plaintextEach variable is plain text, sensitive (hidden in build logs) or secret (never readable outside Expo's servers). Files such as Firebase's google-services.json can be uploaded as file variables; the build receives a file path, which you reference from app.config.js:
export default {
expo: {
android: { googleServicesFile: process.env.GOOGLE_SERVICES_JSON ?? "./google-services.json" },
},
};One caution from Expo's own documentation: anything that ends up in client code is public, whatever its visibility setting. Keep real secrets on your server.
Hooks, CI and auto-submit
Lifecycle hooks are package.json scripts that EAS runs at fixed points: eas-build-pre-install, eas-build-post-install (after install, prebuild and, on iOS, pod install), eas-build-on-success, eas-build-on-error, eas-build-on-complete and eas-build-on-cancel. They're the place for steps like generating a config file or notifying a chat channel when a build fails.
CI builds use the same CLI without prompts. In GitHub Actions, add an EXPO_TOKEN secret, then:
eas build --platform all --profile production --non-interactive --no-wait--no-wait returns as soon as the build is queued; watch its status on the Expo dashboard or with eas build:list.
Auto-submit sends the finished build straight to the stores: eas build --platform ios --profile production --auto-submit uploads the iOS build to App Store Connect, where it appears in TestFlight once Apple finishes processing it.
EAS Build vs Expo Go vs building in Xcode
| Expo Go | EAS development build | Local Xcode or Android Studio build | |
|---|---|---|---|
| What it is | A ready-made app from the stores that runs your JavaScript | Your own app, with your native code and dev tools | A native build on your machine |
| Custom native code | Only the modules bundled into Expo Go | Yes | Yes |
| Needs a Mac for iOS | No | No (cloud) | Yes |
| Signing | Not needed | Managed by EAS | Manual or Xcode-managed |
| Best for | Quick experiments and learning | Day-to-day development and testing | Debugging native build problems |
Expo Go supports one SDK version at a time and, since September 2026, asks you to sign in to an Expo account. As soon as a project needs a library with its own native code, development builds are the better default.
What it costs (October 2026)
| Plan | Price | Builds | Queue |
|---|---|---|---|
| Free | $0 | Up to 15 Android and 15 iOS builds a month | Low priority (Expo warns of 90+ minute waits at peak) |
| Starter | $19/month plus usage | $45 of build credit, then pay per build | High priority |
| Production | $199/month plus usage | $225 of build credit, then pay per build | High priority, 2 concurrent builds |
| Enterprise | Custom | From $1,000 of build credit | High priority, 5 concurrent builds |
Beyond the included credit, builds cost $1 (Android, medium worker) to $2 (large) and $2 (iOS, medium) to $4 (large). For a small team shipping weekly, Starter usually covers it. Prices change, so check Expo's pricing page before you budget.
From the field: Expo's build and submit services are part of the release path I own on the apps I ship, alongside EAS Update for over-the-air hotfixes and Fastlane with GitHub Actions where a project needs its own pipeline. The biggest win isn't the cloud machines. It's that the whole release process lives in a config file anyone on the team can read.
Common build failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
Build fails during pod install | Native dependency version conflicts | Align versions with npx expo install --fix; check the library's iOS requirements |
| Android build runs out of memory | Large project on a medium worker | Use a large worker in the profile, or trim dependencies |
| Works locally, fails on EAS | Missing environment variables or secrets | Add them as EAS environment variables for that profile |
| Upload rejected for duplicate build number | Local versioning out of sync | Switch to remote versioning with autoIncrement |
| App crashes on launch, dev build fine | Release-only issue (Hermes bytecode, missing config) | Test a preview build before every production build |
FAQ
Is EAS Build free?
There's a free plan with up to 15 iOS and 15 Android builds a month on a low-priority queue, which can be slow at busy times. Paid plans start at $19 a month with build credit and priority builds, and you can always build locally for free.
Do I need a Mac to build an iOS app with Expo?
Not if you build in Expo's cloud, which compiles iOS apps for you. You need a Mac only to build iOS locally (including eas build --local for iOS) or to run the iOS simulator.
Can I use EAS Build with a bare React Native project?
Yes. It works with bare projects that keep their own ios and android folders, as well as projects that use Expo's Continuous Native Generation. Add an eas.json and run eas build.
What's the difference between EAS Build and EAS Update?
A build creates full native app binaries that go through store review. EAS Update ships JavaScript and asset changes over the air to builds that are already installed, without a new store release, as long as the native code hasn't changed.
Next steps
If your builds are slow, fragile or depend on one person's laptop, I can set up EAS (or your own CI) so a release is one command anyone on the team can run. Get in touch, or see my services.
Sources
- EAS Build introduction, Expo
- Configure builds with eas.json, Expo
- App credentials, Expo
- App version management, Expo
- Expo pricing, Expo
- npm lifecycle hooks, Expo
- Environment variables in EAS, Expo
Related
Versioning a React Native App: versionCode, buildNumber, runtimeVersion
React Native app versioning explained: version vs build number on iOS and Android, runtimeVersion for OTA updates, auto-increment and update checks.
Code Signing Demystified: Certificates, Provisioning Profiles and Keystores
React Native code signing explained: iOS certificates and provisioning profiles, Android keystores, upload vs app signing keys, and how EAS manages it all.
Fastlane for React Native in 2026: When You Still Need It
Fastlane for React Native in 2026: what it automates, when EAS replaces it, when you still need it, and a working Fastfile for TestFlight and Google Play.
