skip to content

An EAS Update hotfix published from CI makes production builds call the staging API; what does eas update's --environment flag change, and how do you prevent this?

level: seniorimportance: should knowfreq 30%

answer

  1. values baked in at export time
  2. --environment loads EAS variables
  3. local .env ignored with the flag
  4. check skipped when CI is set
  5. match the build's environment

basics

~20 s

An API URL read at bundle time is baked into the update. --environment production makes eas update bundle with that EAS environment's variables and ignore local .env files; a CI job without it uses the runner's values.

solid answer

~40 s

Public configuration like an API URL is read at **export time** and baked into the JavaScript bundle, so an update carries the values of the environment it was bundled in, not the build's. `eas update --environment production` loads the **production** EAS environment variables for the export, disables local `.env` loading and clears the bundler cache. EAS CLI's help marks the flag as required for SDK 55 and later, but the interactive prompt is skipped when `CI` or `EAS_BUILD` is set, so a CI job that omits it silently bundles with the runner's variables, such as a checked-in staging `.env`. Prevent it by always passing `--environment` matching the build profile's environment, promoting tested updates with `update:republish` instead of re-exporting, and protecting the production channel with `eas channel:protect` so only account admins can publish there.

code

bash · 6 lines
bash
# CI step: export with the production EAS environment, not the runner's .env
eas update --channel production --environment production \
  --message "$COMMIT_MESSAGE" --non-interactive

# once: only account admins may publish to production
eas channel:protect production

go deeper

for a junior

Recall that an update is a new JavaScript bundle, so values baked in when it is exported travel with it.

for a middle

Explain what --environment loads, why local .env files are ignored with it, and why the build profile and update should use the same environment.

for a senior

Diagnose the CI gap where the prompt is skipped, and harden the process with explicit flags, promotion by republish and a protected production channel.

for a principal

Set the guardrails for over-the-air releases: who can publish to production, which environments exist, and what must never be inlined into a bundle.

## Why an update can carry the wrong configuration An **EAS Update** is a JavaScript bundle plus assets, produced by exporting the project on the machine that runs `eas update`. Configuration that the app reads from environment variables at **bundle time** — typically public values like an API base URL — is inlined into that bundle. The installed binary does not re-supply those values; the update brings its own. So an update exported with staging variables points production users at staging, even though every build was configured correctly. ## What --environment does `eas update --environment <name>` names an **EAS environment** — usually `production`, `preview` or `development` — whose server-side environment variables EAS CLI loads before exporting. Reading `eas-cli`'s update command: - the environment's variables are fetched from EAS and applied to the export; - local `.env` loading is switched off for that export, so a stray `.env` in the repository cannot leak in; - the bundler cache is cleared, so previously inlined values are not reused. EAS CLI's help text marks `--environment` as **required for projects on Expo SDK 55 or later**, which includes SDK 57. ## The CI gap The requirement is enforced interactively: without the flag, EAS CLI prompts you to choose an environment. The check is **skipped** when the `CI` or `EAS_BUILD` environment variable is set or `--auto` is used, and can be bypassed with `EAS_UPDATE_SKIP_ENVIRONMENT_CHECK`. In a CI job without `--environment`, no prompt appears and no EAS variables are loaded; the export uses whatever the runner provides, including any committed `.env`. That is the usual root cause of the staging-API incident. ## Preventing it 1. **Always pass `--environment`** in CI, matching the environment the production build profile uses. 2. **Promote instead of re-exporting.** Publish once to staging, verify, then `eas update:republish --destination-channel production`, so production gets the exact bundle that was tested. Expo recommends identical variables on staging and production when you do this. 3. **Protect the production channel.** `eas channel:protect production` restricts publishing to that channel to account admins, so a misconfigured job under a less privileged account cannot publish there. 4. **Keep secrets out of the bundle entirely.** Anything inlined into an update is readable by anyone who downloads it. ## A CI template that avoids the gap 1. Name the environment explicitly: `--environment production` for production publishes, `--environment preview` for previews. 2. Pass `--non-interactive` and a `--message`, so the job fails loudly instead of prompting. 3. Keep `.env` files with real endpoints out of the repository, so a misconfigured job has nothing wrong to pick up. 4. Publish to staging first, then promote with `update:republish`; the production publish never re-exports. ## Checking which values an update carries | Symptom | Likely cause | Check | |---|---|---| | production app hits staging API after an update | export used staging or local variables | the CI command line; is `--environment` present? | | values differ between build and update | build profile and update used different environments | build profile's environment vs the flag | | problem only on devices that updated | the update, not the binary | roll back or republish the previous update | ## Recovering Recovery is an update problem, not a build problem: put the previous good update back on production's branch (for example with `eas update:republish` of the last good group, or the rollback commands), then publish a corrected export with the right `--environment`. Tools for gradual exposure and automatic revert are separate from the channel mechanics. ## Summary - Bundle-time configuration travels **with the update**. - `--environment` decides which EAS variables that bundle is exported with and shuts out local `.env` files. - CI skips the prompt, so the flag must be explicit there. - Republishing the tested update and protecting the production channel close the remaining gaps.

  • Why does a correct production build not protect you from an update exported with staging values?
    Values read at bundle time are inlined into the update's JavaScript. When the build downloads the update, it runs that new bundle with the values it carries; the build's own configuration applies only to its embedded bundle.
  • What does eas channel:protect add to a production release process?
    It restricts publishing to that channel to account admins. A CI job or developer without admin rights can no longer publish straight to production, which pushes routine changes through staging and promotion.

saying these in an interview costs you the question

  • The installed build re-injects its own environment into downloaded updates
  • Without --environment, CI always fails with a prompt error
  • --environment changes the runtime version of the update
  • A local .env is still merged in when --environment is passed
  • Secrets inlined into an update bundle stay private