Why OTA updates matter

App store releases take 24–72 hours to review. For critical bug fixes — a broken payment flow, a crash on a specific device — that's an eternity. Expo's OTA (over-the-air) updates let you ship JavaScript bundle changes instantly, bypassing the review cycle entirely.

Across the mobile projects we've maintained, OTA updates have let us ship critical fixes in under an hour at least a dozen times. But they also introduce a new class of risk if you're not careful about how you manage channels and rollbacks.

Understanding Expo update channels

Expo EAS Update gives you named channels. We use three: development, staging, and production. The channel is set in the build configuration, not at runtime — a production binary always pulls from the production channel.

This means you can push a new update to staging and test it with your QA build without affecting any production users. Obvious in retrospect, but we've seen teams ship to production directly from their development branch.

The branching model we use

Each EAS Update channel maps to a Git branch: mainproduction,stagingstaging. Merging to main triggers a GitHub Actions workflow that runs eas update --channel production --message "...".

We add a mandatory approval step in the workflow for production updates. One engineer pushes; another approves in GitHub. This catches the "I meant to push to staging" mistake.

Rollbacks

Expo doesn't have a one-button rollback, but the model is simple: re-publish an older bundle to the channel. We keep the last three update IDs in a releases.json in the repo. If a production update causes issues, we run:

eas update --channel production --republish --group [previous-group-id]

Users get the old bundle on their next app open (or within 60 seconds if the app is backgrounded, depending on your checkAutomatically setting).

What OTA updates can't fix

OTA updates only cover JavaScript changes. Native module changes — a new Expo SDK version, a new native dependency, permissions changes — require a full app store release. We maintain a clear internal rule: if the change touches app.json, package.json native deps, or any file under android/ or ios/, it needs a native build.

Forgetting this rule is the most common source of mysterious crashes after an OTA update.

Monitoring update adoption

Expo's dashboard shows update adoption rates per channel. We check this after every production push. If adoption is stuck at 60% after 48 hours, it usually means a segment of users has the app backgrounded or has disabled auto-updates. For critical fixes, we add an in-app prompt to restart.