EAS Update in 2026: When OTA Saves You and When It Gets You Rejected
OTA updates are the best part of Expo — until they aren't. A field guide to what you can safely ship over-the-air in 2026, what will get your app pulled, and how we structure release channels to sleep at night.

Microsoft killed CodePush. Expo's EAS Update quietly became the default way React Native teams patch production without a store review. That's a superpower — and, if you misuse it, a very fast path to a developer account suspension.
We've been shipping OTA updates through EAS across a few dozen apps since 2022, and the rules of the game have shifted noticeably in the last year. Here's what actually holds up in 2026.
Why OTA still matters, even in a post-CodePush world
Store review times are better than they were, but "better" still means 24 hours on a bad day and multi-day when reviewers push back. If your checkout flow throws on a null field or your onboarding regresses conversion by 12%, waiting on review is not a plan.
EAS Update lets you push a new JavaScript bundle (and most JS-adjacent assets) directly to installed apps on next launch. In our experience, most user devices pick up the update within 24–48 hours of publish, and you can gate it by channel, runtime version, or a rollout percentage.
What you get:
- Same-day hotfixes for JS-only regressions
- Server-driven rollouts, including cohort targeting
- Free rollback by republishing the previous update to the same channel
- No binary rebuild, no TestFlight, no reviewer sighing at your screenshots
What you don't get, and this is where teams get burned: the ability to change anything native. Not a permission string. Not a native module version. Not a config plugin behaviour. That's the whole story of this article.
The runtime version is the contract
Every EAS Update is bound to a runtimeVersion. The update will only be delivered to app binaries that were built with the same runtime version. This is the mechanism that stops you from shipping a JS bundle that calls a native API the installed binary doesn't have.
In 2026, runtimeVersion: { policy: "fingerprint" } is what most teams should use. Expo computes a fingerprint of your native project — dependencies, config plugins, native code, app.json values that affect native — and bumps the runtime version automatically when any of that changes.
{
"expo": {
"runtimeVersion": { "policy": "fingerprint" },
"updates": {
"url": "https://u.expo.dev/your-project-id",
"fallbackToCacheTimeout": 0,
"checkAutomatically": "ON_LOAD"
}
}
}
With fingerprint policy, the mental model becomes simple:
- Changed only JS/TS/assets? Same fingerprint.
eas updatewill reach existing installs. - Changed a native dep, a config plugin, or an entitlement? New fingerprint. You must ship a new binary through the stores. OTA will not save you.
The number of Slack messages we've seen along the lines of "the update went out but the crash is still happening" is almost always a runtime version mismatch. Check that first.
When to override the fingerprint
Sometimes you know a native change is safe — a dev-only dependency, a change to a build script that doesn't touch runtime. You can pin a manual runtimeVersion string and take responsibility yourself. We only do this on internal tooling apps. On production consumer apps, trust the fingerprint.
What Apple and Google actually allow
Here's the part people misread. OTA updates are permitted, but with limits, and those limits are enforced unevenly — which is worse than being enforced strictly, because it means you can get away with it for months before you don't.
The Apple rule that matters
Apple's Developer Program License Agreement (specifically the section governing interpreted code) permits updates delivered through Apple's approved JavaScriptCore or WebKit environments, provided the updated code:
- Does not materially change the primary purpose of the app as reviewed
- Does not introduce features, monetisation, or content the reviewer didn't see
- Does not deliver executable code from outside the approved runtime
React Native's JS runtime satisfies point 3. Points 1 and 2 are on you.
What that means in practice:
- Safe: bug fixes, copy changes, layout tweaks, tuning a feature flag default, changing an API endpoint, swapping analytics events.
- Risky: enabling a whole new feature area that wasn't in the reviewed build, changing your pricing model, adding a paywall to a previously free flow, unhiding an admin surface.
- Guaranteed rejection or takedown if noticed: shipping a completely different app under the same binary (yes, people try this), enabling gambling/adult content post-review, adding a payment method that bypasses IAP.
Google is looser in policy but stricter in automated detection. Play Protect will flag apps whose behaviour drifts far from what was scanned at upload. We've seen apps get "suspicious behaviour" warnings surfaced to users after aggressive OTA changes.
The unwritten rule
If a reviewer would have had a follow-up question about the feature in your OTA update, you should have shipped it as a new binary. That's the heuristic we give clients.
A channel strategy that doesn't collapse under pressure
EAS channels are cheap. Use lots of them. The setup we recommend for any team past a solo founder:
# Channels
# - development -> internal dev clients
# - preview -> internal QA + stakeholder builds
# - staging -> release candidates, points at prod-like backend
# - production -> live App Store / Play Store builds
Map each build profile in eas.json to exactly one channel. Never share channels across profiles.
{
"build": {
"preview": { "channel": "preview", "distribution": "internal" },
"staging": { "channel": "staging", "distribution": "internal" },
"production": { "channel": "production", "autoIncrement": true }
}
}
Then publish updates explicitly:
# Ship a hotfix to production, current branch
eas update --channel production --message "fix: null guard on checkout summary"
# Roll out to 10% first
eas update --channel production --rollout-percentage 10
Rollouts are the feature we lean on hardest. Ship to 10%, watch Sentry and your product analytics for an hour, then bump to 50% and 100%. If something's wrong, republish the previous update and it propagates on next app launch.
Don't skip the rollback drill
Once a quarter, we run a rollback drill in staging. Publish a deliberately broken update, verify Sentry catches it, republish the prior update, confirm devices recover. Teams that haven't practiced this will fumble the one time it matters. If you'd like a hand designing that muscle into your release process, our mobile team does this kind of setup regularly.
Common OTA war stories
A sampler of things we've watched go wrong.
The permission string swap. Team added a new NSCameraUsageDescription in app.json and shipped it via OTA. It's a native config change, so the JS bundle was delivered but the underlying binary still had the old string. iOS presented the old copy, App Store review the next month flagged the mismatch. Two week delay.
The silent feature flag flip. Company toggled a paywall on for all users via a remote config value that was really an OTA-shipped constant. Apple didn't catch it. Users did, App Store reviews tanked, refund rate tripled. The team had to ship a binary undoing it and eat the reputation hit.
The runtime version drift. Someone bumped a native module in a hotfix branch and shipped an OTA update against the old fingerprint. Devices on the old binary crashed on launch because the JS bundle imported symbols the native side didn't export. Rollback fixed it in an hour, but the postmortem was ugly.
The stale asset. A designer swapped a large image asset. The update was published, but fallbackToCacheTimeout: 0 combined with a slow connection meant users saw the old asset for a session. Not a disaster — but worth knowing that assets larger than a couple of megabytes have real download-time consequences on next launch.
The Swift/Kotlin honesty check
If your app is 90% native modules with a thin React Native shell, OTA gives you very little. You'll be rebuilding the binary for almost every change anyway. Teams in that shape sometimes ask us whether to just go native.
Our answer is boring: measure the ratio of your last twenty production fixes. If more than 70% were JS-only, EAS Update is paying for itself many times over. If it's under 30%, you're carrying the complexity of a hybrid stack without the payoff, and native Swift/Kotlin — or at least a much thinner JS layer — deserves a serious look.
Where we'd start
If you're setting up EAS Update on a new project this quarter:
- Turn on fingerprint runtime versioning from day one.
- Create four channels, wire them to build profiles, and never cross the streams.
- Default every production update to a 10% rollout with a manual bump.
- Write a one-page policy of what your team is and isn't allowed to ship over-the-air. Pin it in your repo.
- Do the rollback drill before you need it.
OTA is the closest thing mobile has to the web's deploy-anytime posture. Treat it with the same discipline you'd treat a production database migration, and it'll be the feature you brag about. Treat it as a way to skip review, and it'll be the feature that ends the app.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading

Biometric Auth in React Native: Face ID, Android Keystore, and the Silent Lockouts Nobody Warns You About
Face ID and fingerprint auth look like a two-line integration until users start getting locked out after an OS update. Here's how we ship biometric login in React Native without the 3am support tickets.

Push Notifications in React Native 2026: Expo Push, FCM HTTP v1, and APNs Tokens That Silently Expire
Push looks simple until 30% of your Android sends fail overnight and iOS silent notifications stop waking your app. Here's how the stack actually breaks in 2026 and how we wire it.
In-App Purchases in React Native: RevenueCat, StoreKit 2, and the Receipts That Lie
A practical breakdown of wiring IAP and subscriptions into a React Native app in 2026 — what RevenueCat actually saves you, where StoreKit 2 still bites, and the receipt edge cases that cost real revenue.
