Multiple environments
Set up development, preview, and production environment variables for the mobile app.
For the complete documentation index, see llms.txt. Prefer markdown by appending.mdto documentation URLs or sendingAccept: text/markdown.
Mobile apps need a little more care than web apps because values can be baked into the native binary or JavaScript update. Keep each environment isolated so test builds never talk to production services by accident.
The safe pattern is:
- use
development,preview, andproductionas your EAS environments - expose only app-safe values with
EXPO_PUBLIC_ - keep backend secrets on the web/API side
- use different bundle identifiers for non-production builds
Public means public
Every EXPO_PUBLIC_ value can be read from the compiled app. Use it for URLs, feature flags, and public project keys only. Never put secret API keys or private tokens in the mobile app.
Choose your environment values
Start with the variables the app needs to know at runtime:
EXPO_PUBLIC_APP_ENV="development"
EXPO_PUBLIC_SITE_URL="http://localhost:3000"
EXPO_PUBLIC_DEFAULT_LOCALE="en"
EXPO_PUBLIC_AUTH_PASSWORD="true"
EXPO_PUBLIC_THEME_MODE="system"
EXPO_PUBLIC_THEME_COLOR="orange"Use the same names for preview and production. Only the values should change.
Use local files for development
For local development, create apps/mobile/.env.local:
EXPO_PUBLIC_APP_ENV="development"
EXPO_PUBLIC_SITE_URL="http://192.168.1.10:3000"
EXPO_PUBLIC_DEFAULT_LOCALE="en"
EXPO_PUBLIC_AUTH_PASSWORD="true"
EXPO_PUBLIC_THEME_MODE="system"
EXPO_PUBLIC_THEME_COLOR="orange"Use your machine's LAN IP when testing on a physical device, so the app can reach your local web/API server.
Avoid NODE_ENV switching
Expo recommends not using NODE_ENV to switch app environments. For EAS builds and updates, use EAS environments instead.
Create EAS environments
Create the same variables in EAS for each environment:
cd apps/mobile
eas env:create --environment development --name EXPO_PUBLIC_SITE_URL --value http://192.168.1.10:3000 --visibility plaintext
eas env:create --environment preview --name EXPO_PUBLIC_SITE_URL --value https://staging.example.com --visibility plaintext
eas env:create --environment production --name EXPO_PUBLIC_SITE_URL --value https://example.com --visibility plaintextRepeat for each EXPO_PUBLIC_ value the app needs.
If you want to test an EAS environment locally, pull it into .env.local:
eas env:pull --environment previewMap build profiles to environments
Make sure each EAS build profile points to the right environment:
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"environment": "development",
"channel": "development"
},
"preview": {
"distribution": "internal",
"environment": "preview",
"channel": "preview"
},
"production": {
"environment": "production",
"channel": "production"
}
}
}Then build with the matching profile:
pnpm --filter mobile eas build --profile preview
pnpm --filter mobile eas build --profile productionKeep app variants separate
Use a different app name, iOS bundle identifier, and Android package for non-production builds:
const appEnv = process.env.EXPO_PUBLIC_APP_ENV ?? "development";
const isProduction = appEnv === "production";
export default {
name: isProduction ? "Acme" : `Acme (${appEnv})`,
slug: "acme",
ios: {
bundleIdentifier: isProduction ? "com.acme.app" : `com.acme.app.${appEnv}`,
},
android: {
package: isProduction ? "com.acme.app" : `com.acme.app.${appEnv}`,
},
};This lets you install preview and production builds on the same device, use separate native credentials, and avoid mixing push notifications or purchases.
Match updates to environments
When publishing an update, pass the environment and channel together:
pnpm --filter mobile eas update --environment preview --channel preview --message "Preview update"
pnpm --filter mobile eas update --environment production --channel production --message "Production update"Before release, verify:
EXPO_PUBLIC_SITE_URLpoints to the matching web/API environment- OAuth redirect URLs are registered for that build variant
- Sentry/PostHog/RevenueCat/Superwall keys match the environment
- app store sandbox settings are used outside production
- no private secrets are present in
EXPO_PUBLIC_values
Useful references
How is this guide?
Last updated on