EAS Build & Deploy

Complete guide for building and submitting your app using Expo Application Services (EAS). This page also explains how EAS fits into the current Ship React Native backend story: Convex is the recommended full backend, while Expo API Routes are the lighter server-side option.

Prerequisites

RequirementDetails
Expo accountexpo.dev/signup
EAS CLInpm install -g eas-cli
Apple Developer accountRequired for iOS — $99/year
Google Play accountRequired for Android — $25 one-time

Initial Setup

# Login to EAS
eas login

# Configure project (creates eas.json)
eas build:configure

Build Profiles

eas.json
{
  "cli": {
    "version": ">= 12.0.0",
    "appVersionSource": "remote"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "ios": { "simulator": true }
    },
    "preview": {
      "distribution": "internal",
      "android": { "buildType": "apk" }
    },
    "production": {
      "autoIncrement": true
    }
  },
  "submit": {
    "production": {
      "ios": {
        "appleId": "your@email.com",
        "ascAppId": "1234567890",
        "appleTeamId": "ABCDEF1234"
      }
    }
  }
}
ProfilePurposeDistribution
developmentLocal dev with dev clientInternal (simulator)
previewQA / beta testingInternal (APK / Ad-hoc)
productionApp Store / Play StoreStore

Build Commands

# Preview build (internal testing)
eas build --platform ios --profile preview
eas build --platform android --profile preview

# Production build
eas build --platform ios --profile production
eas build --platform android --profile production
eas build --platform all --profile production

Submit to App Stores

# Submit latest build
eas submit --platform ios
eas submit --platform android

# Submit specific build
eas submit --platform ios --id BUILD_ID

EAS Environment Variables

Critical: SECRET vs PLAINTEXT visibility
EAS env vars set as SECRET are ONLY available during EAS builds (native compile time). EAS Hosting deployed workers (API routes) can ONLY read PLAINTEXT env vars. This distinction caused hours of debugging across 5 production apps.
VariableVisibilityWhy
OPENAI_API_KEYPLAINTEXTMust be readable by the deployed EAS Hosting worker at runtime
ANTHROPIC_API_KEYPLAINTEXTSame — API routes need it at runtime, not build time
EXPO_PUBLIC_REVENUECAT_IOS_KEYPLAINTEXT in eas.jsonEXPO_PUBLIC_* vars are baked into the JS bundle by Metro at build time — EAS Secrets are not available to Metro
SENTRY_AUTH_TOKENSECRETOnly needed during the native build process for source map uploads

Setting environment variables

# Set PLAINTEXT vars (for API routes / EAS Hosting)
eas env:create --name OPENAI_API_KEY --value sk-xxx --visibility plaintext --environment production

# Set SECRET vars (for build-time only)
eas env:create --name SENTRY_AUTH_TOKEN --value sntrys_xxx --visibility secret --environment production

# List all env vars
eas env:list

# EXPO_PUBLIC_ vars go in eas.json, not EAS secrets
# They must be inlined by Metro at bundle time
After setting or changing EAS env vars for API routes, you MUST redeploy: eas deploy --prod --environment production. The deployed worker caches its environment at deploy time.

Backend Strategy

This boilerplate supports two server-side paths. Use Convex when you need auth, persistence, credits, and subscriptions. Use Expo API Routes when you mainly need secure AI proxy endpoints running on EAS Hosting.

Where EAS fits best

BenefitDetail
Great for app distributionBuild, submit, and update native apps with a single workflow
Great for API routesConvenient hosting path when you choose Expo API Routes mode
Simple env managementUseful for build-time config and API route runtime envs
Works alongside ConvexEAS handles app delivery even when Convex is your main backend

When to choose each backend mode

Choose Convex for serious product infrastructure. Choose Expo API Routes for lighter AI-only server needs.

API Routes Deployment

Expo API routes (src/app/api/*+api.ts) run server-side on EAS Hosting. They require a separate deploy step from the native app build.

Prerequisites

  • web.output must be "server" in app.json
  • API keys must be set as PLAINTEXT env vars in EAS (not SECRET)
  • EAS project must be initialized (eas init)

Deploy commands

# Step 1: Export the web build (generates server bundle with API routes)
npx expo export --platform web

# Step 2: Deploy to EAS Hosting
eas deploy --prod --environment production

# Verify deployment
curl https://your-app.expo.app/api/health
API routes are NOT available in Expo Go or local simulators. They only work in EAS Hosting (production) or dev client builds pointed at the production URL. For local development, use dev-server.js or set EXPO_PUBLIC_API_URL in your .env to point to the deployed production URL.

Setting EXPO_PUBLIC_API_URL for development

.env (for dev builds pointing to production API)
# Point dev builds to the deployed EAS Hosting URL
EXPO_PUBLIC_API_URL=https://your-app.expo.app

In production EAS builds, EXPO_PUBLIC_API_URL can be empty — API routes use relative URLs automatically. But during development, you need to point to the deployed URL so your dev client can reach the APIs.

expo-constants Pin (Build Fix)

Critical: Versions of expo-constants above 55.0.4 have a podspec with escaped quotes that break Ruby parsing on EAS build servers. This causes Pod install failed errors.
package.json
{
  "dependencies": {
    "expo-constants": "55.0.4"
  }
}

Pin to EXACT "55.0.4" — not ~55.0.4 or ^55.0.4. The old scripts/patch-expo-constants-podspec.js workaround is no longer needed with this pin.

.easignore (Faster Builds)

Add a .easignore file to exclude heavy folders from the EAS build upload. This significantly speeds up build times.

.easignore
# Heavy folders not needed for the build
node_modules/
.git/
.expo/
ios/Pods/
ios/build/
android/build/
android/app/build/
android/.gradle/

# Working files
assets/source/
docs/
README.md
__tests__/
.husky/
.github/

Troubleshooting

ErrorFix
"No bundle identifier"Set ios.bundleIdentifier in app.json
Signing errorsRun eas credentials to reset
"App ID not found" in ASCCreate the app in App Store Connect first, check bundle ID matches exactly
Android build failseas build --platform android --clear-cache
Pod install failed / podspec parse errorPin expo-constants to exact "55.0.4" in package.json
API route returns 500 / "API key not configured"API key must be PLAINTEXT in EAS env vars, not SECRET. Redeploy after changing.
API routes work in prod but not in devSet EXPO_PUBLIC_API_URL in .env to the deployed EAS Hosting URL
One Command to Ship

EAS configured and ready to submit

eas.json, secrets workflow, and store submission guide all included.

EAS build profiles
Secrets management
App Store submission
Get ShipReactNative
Save 40+ hours of setup