React Native OTA Updates 2026: EAS Update, Rollouts, Rollbacks & CodePush Migration
Konrad Bachowski
Tech lead, HeyNeuron
React Native OTA Updates in 2026: Push Fixes Without Waiting for App Review
A critical JavaScript bug in production. Your users see a broken screen while your fix waits in Apple's review queue. Over-the-air (OTA) updates solve exactly this — you push the fix from your terminal and users get it on next launch, no binary rebuild required.
In 2026 the OTA landscape shifted significantly: Microsoft retired AppCenter CodePush on March 31, 2025, and archived the repository two months later, forcing tens of thousands of teams to migrate. Expo EAS Update became the default path for most, while Bitrise launched a like-for-like CodePush replacement in March 2026. Choosing the right tool — and knowing how to use it safely — matters more than ever.
This guide covers the complete React Native OTA workflow: EAS Update configuration, channel and branch strategy, percentage-based rollouts, rollback procedures, automated CI/CD integration, GDPR compliance, and a step-by-step migration from CodePush.
What OTA Updates Can (and Cannot) Ship
Not every change qualifies for an OTA push. The boundary is the native binary — anything that touches the ios/ or android/ directories requires a full rebuild and store submission.
Safe to ship OTA:
- JavaScript bug fixes and logic changes
- UI copy, color, and layout updates
- New screens or flows built entirely in React Native components
- Asset changes (images, JSON config, fonts)
- Feature flag adjustments
- API endpoint or environment variable changes
Requires a new binary:
- Adding or upgrading native modules
- Changing app permissions or entitlements
- Expo SDK version upgrades
- Modifying iOS Privacy Manifests or Android build.gradle
- Enabling the New Architecture on an existing binary
App Store Policy: Guideline 3.3.2 and Google Play
Apple's App Store Review Guidelines section 3.3.2 explicitly permits OTA updates for JavaScript interpreters and runtime environments, provided the downloaded code does not change the app's primary purpose, does not introduce features not reviewed at submission, and does not bypass in-app purchase for payments.
Rejection and takedown risks:
- Rolling out a new monetization flow or paywall not in the reviewed binary
- Adding gambling or adult content after approval
- Materially changing the app category (e.g., turning a productivity tool into a game)
- Shipping executable native code via an OTA mechanism
Google Play follows similar reasoning under its Developer Program Policies. A good rule of thumb: if you would be nervous describing the change to an App Store reviewer, ship a binary instead. JavaScript bug fixes, copy changes, and layout tweaks are almost always safe.
Choosing Your OTA Tool in 2026
AppCenter CodePush's retirement left three main categories:
One sentence of context before the comparison: pricing reflects 2026 tiers and the MAU threshold where cost escalation becomes significant.
| Tool | Free MAU | Mid-tier (50K MAU) | Self-hosted | CodePush API compatible |
|---|---|---|---|---|
| EAS Update (Expo) | 1,000 | $199/mo Production | No | No |
| Stallion | 10,000 | $51/mo Pro | Yes | No |
| Bitrise CodePush | 100,000 | Custom | No | Yes |
| xprem / hot-updater | Unlimited | Infra cost only | Yes (self-host) | Partial |
EAS Update is the right default if you are already on Expo SDK. It integrates with EAS Build and EAS Workflows, offers the most mature rollout analytics, and Expo's CDN handles global distribution automatically.
Stallion is the cost-effective choice at scale: at 50K MAU it is roughly 4× cheaper than EAS Production. Stallion also offers a self-hosted option for teams with strict data residency requirements.
Bitrise CodePush (GA March 10, 2026) is the drop-in replacement for teams migrating from AppCenter. It preserves the react-native-code-push SDK's deployment-key model, eliminating the need to refactor your update logic. The 100,000 MAU free tier is the most generous in the category.
Self-hosted options (xprem, hot-updater) make sense for enterprise teams that cannot send update metadata to third-party CDNs — a common GDPR requirement for healthcare or financial apps.
Blueprint 1: Setting Up EAS Update from Scratch
Prerequisites: Expo SDK 50+, EAS CLI, an Expo account.
Step 1 — Install and configure:
npx expo install expo-updates
eas update:configure
eas update:configure writes runtimeVersion and the update URL into your app.json and native manifests. After this one-time step, every subsequent OTA push skips the native layer entirely.
Step 2 — Set runtime version policy:
Add this to app.json:
{
"expo": {
"runtimeVersion": {
"policy": "fingerprint"
}
}
}
The fingerprint policy (recommended for 2026) automatically bumps your runtime version whenever native dependencies, config plugins, or entitlements change. This prevents the most common OTA production failure: shipping a JS bundle to a binary it is incompatible with.
Step 3 — Define channels in eas.json:
{
"build": {
"development": { "channel": "development" },
"preview": { "channel": "preview" },
"staging": { "channel": "staging" },
"production": { "channel": "production" }
}
}
Four channels mapped to four build profiles. Updates published to production only reach users who installed a production binary. Internal QA gets preview builds; release candidates go to staging before promotion.
Step 4 — Publish your first update:
eas update --branch production --message "Fix profile screen null pointer"
The EAS CLI bundles your latest JavaScript, uploads it to Expo's CDN, and links it to the production branch. Most devices pick up the update within 24–48 hours on next app launch.
Blueprint 2: Staged Rollouts and Monitoring
Shipping to 100% of users immediately is the fastest way to discover you shipped a bug. Percentage-based rollouts limit blast radius.
The recommended staging sequence:
- Publish at 10% rollout
- Monitor error rate for 60 minutes (Sentry, Bugsnag, or EAS Insights)
- If error rate holds steady → promote to 50%
- After another hour → promote to 100%
# Publish at 10% rollout
eas update --branch production --rollout-percentage 10 --message "Homepage redesign v2"
# Check adoption in EAS dashboard, then promote
eas update:republish --channel production --rollout-percentage 50
eas update:republish --channel production --rollout-percentage 100
Key metrics to watch during rollout:
- JS error rate: Spike above your pre-update baseline → roll back
- Update adoption: Percentage of users on the new bundle (visible in EAS Insights)
- Crash-free sessions: Should not drop more than 1-2% vs the previous 7-day average
- Bundle download success rate: Below 95% suggests CDN or runtimeVersion mismatch
EAS Update's SDK 55 bundle diffing means users download only the diff between their current and new bundle — reducing update payload by 60–80% on average for incremental changes. For a 15MB app with 1 million users, that difference is roughly 9TB of CDN bandwidth per release.
Blueprint 3: Rollback Playbook
When a bad update reaches production, speed matters. EAS Update does not have a dedicated "rollback" command — instead, you republish the last known-good update.
Finding and restoring the last good update:
# List recent updates on the production branch
eas update:list --branch production
# Republish a specific update ID
eas update:republish --group <update-group-id> --branch production
Users get the rolled-back JS bundle on their next app launch — typically within minutes if you push the rollback immediately.
Automated rollback with CI/CD:
Set error rate thresholds in your monitoring tool and trigger a rollback via the EAS CLI when crossed. A minimal GitHub Actions step:
- name: Rollback if error rate exceeds threshold
if: ${{ env.ERROR_RATE > '0.03' }}
run: |
eas update:republish \
--group ${{ env.LAST_GOOD_UPDATE_GROUP_ID }} \
--branch production
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
Tip: Store your last-known-good update group ID as a GitHub Actions variable and update it after each successful 100% rollout. This gives you a one-step automated revert without manual intervention at 2am.
Native binary mismatch — the silent failure:
If a user's binary is older than the runtimeVersion required by your update, EAS Update silently falls back to the embedded bundle. The user sees the version from their last install, not your latest fix. This is why the fingerprint policy matters: it prevents mismatches by refusing to serve incompatible bundles rather than silently falling back.
Blueprint 4: Migrating from CodePush
Microsoft retired AppCenter CodePush on March 31, 2025, and archived the server repository on May 20, 2025. If your app still runs react-native-code-push, it is hitting dead servers and receiving no updates.
Migration path to EAS Update (recommended for Expo projects):
- Remove
react-native-code-pushfrom your dependencies - Install
expo-updatesand runeas update:configure - Replace
CodePush.sync()calls with theuseUpdates()hook fromexpo-updates - Map your CodePush deployment keys to EAS Update channels (
Staging→previewbranch,Production→productionbranch) - Build and submit a new binary to both stores (this is the only mandatory store submission in the migration)
Migration path to Bitrise CodePush (for non-Expo or brownfield projects):
Bitrise CodePush (GA March 2026) preserves the react-native-code-push SDK deployment-key model. Migration requires only a configuration change:
// Before (AppCenter)
const codePushOptions = { serverUrl: "https://codepush.appcenter.ms/" };
// After (Bitrise CodePush)
const codePushOptions = { serverUrl: "https://YOUR_BITRISE_CODEPUSH_URL" };
No SDK changes, no refactoring — swap the server URL and you are live. The Bitrise free tier includes 100,000 MAU, covering most small-to-mid teams at zero cost.
Migration checklist:
- [ ] Audit active deployments — list which CodePush environments have current bundles, screenshot the deployment keys
- [ ] Choose target platform — EAS Update (Expo) vs Bitrise CodePush (drop-in) vs Stallion (cost at scale) vs self-hosted
- [ ] Install new SDK —
expo-updatesor update server URL for Bitrise - [ ] Update CI/CD pipeline — replace
appcenter codepush releasecommands with platform-specific equivalents - [ ] Set runtimeVersion policy —
fingerprintrecommended for EAS, native version for Bitrise - [ ] Build and submit new binary — required once for SDK/manifest changes
- [ ] Test in preview channel — validate update delivery before production cutover
- [ ] Monitor first production push — watch error rates for 2 hours minimum
- [ ] Deprecate old CodePush keys — remove dead environment variables from CI/CD secrets
- [ ] Update runbooks — replace CodePush commands in incident response procedures
EAS Update Pricing: What You Actually Pay at Scale
Pricing at the time of writing (2026):
| Plan | Price/month | MAU | CDN Bandwidth | Storage |
|---|---|---|---|---|
| Free | $0 | 1,000 | 100 GiB | 20 GiB |
| Starter | $19 | 3,000 | 100 GiB | 20 GiB |
| Production | $199 | 50,000 | 1 TiB | 1 TiB |
| Enterprise | Custom | 1,000,000+ | 40 TiB | 10 TiB |
Bandwidth overage: $0.10 per GiB. For a 15MB app with 100,000 users and 4 releases per month (pre-bundle diffing), that is 4.8TB of bandwidth — far beyond the Production plan's 1TB included, adding ~$380/month in overages. SDK 55 bundle diffing reduces this significantly if you push incremental updates.
The MAU definition matters: A MAU is a unique device that downloads at least one update in the billing cycle. Devices on the embedded bundle (no OTA update applied) do not count toward MAU billing — only devices actively receiving OTA pushes.
Decision heuristic: Below 10K MAU → EAS Free tier covers you. At 10K–50K MAU → evaluate Stallion Pro ($51/mo) vs EAS Production ($199/mo). Above 50K MAU → run a bandwidth projection before committing to EAS Production.
GDPR Compliance for OTA Updates
OTA update systems collect device-level metadata: installation IDs, runtime versions, update group IDs, and CDN request logs with IP addresses. Under GDPR, IP addresses are personal data.
5-point GDPR checklist for OTA updates:
- Data Processing Agreement — EAS Update (Expo) operates servers in the US. If you serve EU users, Expo's DPA (available in your Expo account settings) covers this transfer under Standard Contractual Clauses.
- Minimum metadata logging — Avoid logging custom user identifiers alongside update events in your CI/CD pipeline. Use anonymous installation IDs only.
- Right to erasure — OTA systems retain update adoption logs. Review your Expo organization's data retention policy; Enterprise plans allow custom retention windows.
- HIPAA/healthcare apps — EAS Update is not HIPAA-compliant by default. For healthcare apps handling PHI, use a self-hosted solution (xprem, hot-updater) or the Stallion self-hosted option, which keeps all update metadata on your infrastructure.
- Record of Processing Activities (Article 30) — Add OTA update delivery as a processing activity in your RoPA: data subject = app user, data categories = device identifier + IP address, purpose = software update delivery, legal basis = legitimate interest (software maintenance).
When NOT to Use EAS Update
OTA updates are powerful but not universally appropriate. Four scenarios where you should skip OTA:
1. Your app is native-heavy with infrequent JS changes
If fewer than 30% of your last 20 production releases were JS-only changes, the setup and operational complexity of an OTA system exceeds the time savings. A disciplined binary release process with a fast CI/CD pipeline is simpler to maintain.
2. Your app handles payments with tight App Store compliance scrutiny
High-revenue apps (>$100K annual IAP revenue) face closer App Store scrutiny. An OTA rollout that accidentally expands the monetization surface — even innocuously — carries higher takedown risk than the same change would for a small app. Consider shipping monetization changes as binary releases.
3. You need sub-5-minute incident response
OTA updates are not instant. While EAS publishes updates in seconds, most users receive them on next launch — which could be hours after a fix lands. For P0 incidents requiring immediate user impact, a coordinated push notification to force app restart, combined with an OTA fix, is a viable pattern, but it adds complexity.
4. Your team cannot support two update channels operationally
EAS Update requires ongoing maintenance: monitoring adoption rates, watching error rates, managing channel promotion, and handling rollbacks. A 2-person team shipping a simple utility app may find the operational overhead not worth it versus a fast binary release process.
FAQ
How long does it take for users to receive an EAS Update?
Most devices receive OTA updates within 24–48 hours of publication. On next app launch, the expo-updates library checks for a newer bundle and downloads it in the background. The update applies on the following launch (background update model) or immediately if you call Updates.reloadAsync() in your app logic.
Does Expo EAS Update work with the New Architecture?
Yes. EAS Update supports apps using the New Architecture (Fabric + JSI) from Expo SDK 51 onward. The JS bundle format changed with the New Architecture, so make sure your runtimeVersion policy correctly identifies binary compatibility — the fingerprint policy handles this automatically.
What happened to Microsoft AppCenter CodePush?
Microsoft retired AppCenter CodePush on March 31, 2025. The service went dark, and the server repository was archived on May 20, 2025. Teams still running react-native-code-push against the AppCenter URL are receiving no updates. The main migration paths are EAS Update (Expo projects), Bitrise CodePush (drop-in for non-Expo), or Stallion (cost-effective for scale).
Can I use EAS Update without the full Expo SDK?
Yes. EAS Update works with bare React Native projects via the expo-updates library. You do not need to use Expo Go or Expo Router — only the expo-updates native module is required. Run eas update:configure to stamp your native manifests without restructuring your project.
What is runtimeVersion and why does it matter?
runtimeVersion is a string that links a JS bundle to a compatible native binary. If a user's installed binary has runtimeVersion 1.0 and your update requires 2.0, EAS Update will not serve the bundle to that user — it falls back to the embedded bundle. The fingerprint policy automates version bumping based on native dependency changes, preventing the most common OTA compatibility failure.
How do I prevent users from getting stuck on an old broken update?
Republish the last-known-good update group immediately after detecting a bad release. Use the eas update:republish command with the good update's group ID. For persistent issues, a force-reload pattern in your app's error boundary can trigger Updates.reloadAsync() to pull the latest bundle immediately.
What is the difference between EAS Update channels and branches?
A branch holds a sequence of updates (the git-like history of your JS bundles). A channel maps a build profile to a branch — e.g., your production channel always serves the production branch. You can point the production channel to a different branch for staged testing, then point it back. Channels are build-time config; branches are the runtime update source.
How much does OTA update delivery cost per user?
At EAS Update Production pricing ($199/mo), you get 50,000 MAU included. With bundle diffing (SDK 55+), incremental update payloads are 60–80% smaller, so bandwidth overages are much lower than pre-2026 calculations suggest. For cost-sensitive apps above 10K MAU, Stallion Pro at $51/mo for 50K MAU is roughly 4× cheaper than EAS Production.
Conclusion
OTA updates are one of the highest-ROI investments in a React Native team's release process: fix a production crash in minutes instead of waiting for App Review, ship UI iterations without friction, and test with 10% of users before a full rollout.
The CodePush retirement in 2025 forced many teams to revisit their OTA strategy. In 2026, EAS Update is the default for Expo projects, Bitrise CodePush is the drop-in for non-Expo teams, and Stallion or self-hosted options make sense when cost or data residency requirements push you off managed CDNs.
If you need help setting up an OTA pipeline for your React Native app — or migrating an existing CodePush setup — HeyNeuron's mobile app team has built and deployed EAS Update workflows across production apps.
Related resources:
- React Native CI/CD Pipeline with GitHub Actions and EAS Build
- How to Deploy a React Native App to the App Store with EAS
- React Native Testing Guide: Jest, Detox, and Maestro
- React Native Performance Optimization
- React Native App Architecture Guide
- Mobile App MVP Cost 2026
- How to Choose the Right Tech Stack
- Mobile App Development Services
Stay up to date with AI and automation
Subscribe to our newsletter to receive specific tips and tools once a week. Join over 2,000 subscribers.