Migrations · OTA Updates

CodePush is dead. Here's what actually works for React Native OTA updates.

Published April 26, 2026 · 11 minute read
TL;DR

Microsoft retired App Center on March 31, 2025. The hosted CodePush service went with it. The client SDK still exists on npm, but there's nothing to call.

Three real paths forward: Expo's EAS Update (works in bare React Native via expo-updates), self-hosting an open-source CodePush server, or a smaller hosted alternative like Pushy or hot-updater.

If you're still calling the old App Center endpoint, your users haven't received an update in over a year. Check first. Pick a destination second.

Microsoft announced App Center's retirement in April 2024. The service went dark on March 31, 2025. CodePush was bundled into App Center, so it died on the same day.

There was no successor. Microsoft pointed users at GitHub Actions for CI, App Insights for analytics, and a vague "consider alternatives" page for CodePush. Teams that built their release process around it had a year to migrate. Many didn't.

This post is what to do now. The options that exist, what they cost, what they require, and which one to pick based on what your app actually needs.

On this page
  1. First: are your users still getting updates?
  2. What exactly died, and what didn't
  3. The real options in 2026
  4. Option 1: EAS Update (bare workflow)
  5. Option 2: Self-host an open-source server
  6. Option 3: Smaller hosted services
  7. Option 4: Roll your own
  8. Apple and Google policy: what OTA updates can actually do
  9. How to pick
  10. FAQ

First: are your users still getting updates?

Before you read about migration, check whether you have a live problem. Open your app's AppDelegate.m (or AppDelegate.swift) and your MainApplication.java. Look for any reference to CodePush, CodePushUpdateManager, or an App Center deployment key.

If you find one, run this against the App Center API base URL:

curl -I https://codepush.appcenter.ms/v0.1/public/codepush/update_check

You'll get a connection error or a 404. That's the dead endpoint. Every install of your app pointing at it has been quietly running the bundle it was last shipped with — for over a year now, depending on when you last released.

This is the silent failure mode of the migration. The app works. Crashes don't spike. Users don't email support. They just stop receiving the JavaScript fixes you push. The version on their phone is whatever was current when CodePush stopped responding.

What exactly died, and what didn't

The hosted CodePush service died. That's the bit Microsoft ran — the API, the CLI, the web dashboard, the rollout controls. All gone.

The React Native client SDK didn't die. react-native-code-push is still on npm. The source is still on GitHub under Microsoft's org, in archived mode. The Objective-C and Java code that fetches and swaps bundles still works. It just needs something to talk to.

The CodePush server source code didn't die either. Microsoft open-sourced it. There are at least two actively maintained forks: lisong/code-push-server and microsoft/code-push-server (archived, but the code is still readable). You can stand one up yourself.

The CodePush CLI died with the service it talked to. You'd need a fork or a new client built against your self-hosted server.

Practical implication

If you have a React Native app shipped with react-native-code-push wired up, you don't need to rip out the SDK. You need to either point it at a new server or replace it with a different OTA library. Most teams replace it. The SDK was built around App Center's auth model, which doesn't survive the migration cleanly.

The real options in 2026

Four real paths. Pick one based on what your team actually needs.

OptionBest forWatch out for
EAS UpdateMost teams. Bare or managed Expo, modern React Native, want hosted with rolloutsAdds expo-updates native module. Ties you to Expo's release cadence
Self-hosted code-push-serverCompliance, data residency, or you already run infrastructureOperational tail — staged rollouts, monitoring, on-call
Pushy / hot-updater / smaller hostedYou want hosted, but EAS doesn't fit (cost, region, vendor preference)Smaller ecosystems. Fewer integrations
Roll your ownYou have very specific requirements and engineering capacityYou'll rebuild EAS Update over 18 months

Option 1: EAS Update (bare workflow)

EAS Update is Expo's hosted OTA service. It works in bare React Native apps — you don't have to adopt Expo's managed workflow.

What you do:

  1. Install expo and expo-updates in your bare project. Yes, Expo in a non-Expo app — the modules can coexist.
  2. Run npx expo install expo-updates and follow the expo-updates bare install docs. Native config edits: a few lines in Info.plist, AndroidManifest.xml, and your app's bridge setup.
  3. Log into EAS, create a project, get an update channel.
  4. Run eas update --branch production to publish a new JavaScript bundle.

The trade-off you're making: expo-updates is a real native module. It increases your app size by a few hundred KB. It adds a dependency on Expo's release cycle — when they bump expo-updates, you'll eventually need to follow. You're not adopting the managed workflow, but you are adopting a piece of the Expo platform.

For most teams this is fine. Expo maintains the library, the documentation is current, the rollout controls (channels, branches, runtime versions) are exactly what CodePush gave you.

Pricing as of 2026: free tier with 1,000 monthly active updates, then per-update pricing. Read the current page before committing — Expo has adjusted it several times.

What "runtime version" means and why it matters

EAS Update enforces a runtime version on every update. A bundle published for runtime 1.2.0 won't install on an app shipping runtime 1.1.0. This is how it prevents the classic OTA disaster: pushing a JS bundle that calls a native API the installed binary doesn't have.

You set the runtime version in app.json (or expo-updates config). Bump it every time you change native code. Forget to bump, and you can ship a JS update that bricks the previous binary on launch. Bump unnecessarily, and you split your audience.

CodePush had something similar with target binary version. The mechanism is the same. Get it right.

Option 2: Self-host an open-source server

The path for teams that can't use a hosted service. Compliance constraints, data residency requirements, an enterprise customer that needs everything in their VPC.

lisong/code-push-server is the most actively maintained fork. Node.js backend, MySQL or PostgreSQL, S3-compatible storage for bundles. It implements the CodePush API surface, so the existing react-native-code-push client works against it once you swap the endpoint.

What you sign up for:

Self-hosting makes sense when the alternative is "we can't use OTA at all." It doesn't make sense as a cost-saving move. The engineering time costs more than EAS Update for nearly every team.

Option 3: Smaller hosted services

A handful of hosted alternatives exist. The ones worth a serious look:

Pick from this group only if you have a concrete reason EAS Update doesn't fit. The smaller the ecosystem, the more time you'll spend integrating versus shipping.

Option 4: Roll your own

A minimum viable OTA service is three things: a signed JavaScript bundle, an HTTPS endpoint that returns the current bundle's manifest, and a CDN that serves the bundle itself.

On the client side, React Native exposes the bundle loader. You can read the current bundle hash on app launch, fetch the manifest, compare, download the new bundle to disk, and reload. There are existing libraries that do most of this — react-native-bundle-fetcher-style packages, or you can write it against RCTBundleURLProvider directly.

The hard parts aren't the happy path. They're:

Each of those is a few weeks of work. The full system is a quarter of an engineer-year for a year, then ongoing maintenance. EAS Update is a hosted product because OTA done well isn't a weekend project.

Reality check

Teams that roll their own usually do so because the first version they shipped — six months ago, before they really understood the problem — became impossible to migrate off. The right time to evaluate "build vs buy" is before you have 500,000 installs running the homegrown version.

Apple and Google policy: what OTA updates can actually do

Apple's App Store Review Guideline 3.3.2 allows scripted content updates as long as the change "does not result in a binary that materially alters the purpose of the app." That's the legal language. The practical interpretation:

Google Play has equivalent language in its Device and Network Abuse policy. Same shape, same limits.

The thing that gets apps pulled isn't the technical capability of OTA — it's shipping new functionality that should have gone through review. If your OTA channel becomes "the way we ship features," your next major release will get flagged. Use it for fixes and tuning. Ship features through the stores.

How to pick

Decision tree:

  1. Are you bare React Native and not blocked by compliance? Use EAS Update. It's the path most teams should take.
  2. Are you blocked by compliance (data residency, HIPAA, FedRAMP)? Self-host lisong/code-push-server or evaluate hot-updater on infrastructure you control.
  3. Do you have a specific reason EAS doesn't work (pricing at huge scale, an enterprise customer with infrastructure mandates)? Look at hot-updater, then Pushy, then build.
  4. Are you on Expo managed workflow? EAS Update is the default. There's no decision to make.

The wrong answer for almost everyone is "roll your own." It's the answer that feels engineering-pure and ends up being a multi-year side project that competes with shipping features.

Still on a CodePush-shaped release process?

Migrating off the App Center-era OTA stack is usually wrapped up in a broader React Native upgrade — your code-push integration, your expo-updates compatibility, and your runtime version strategy all touch the upgrade plan. Run the free scanner against your lockfile to see where the dependencies stand.

Frequently Asked Questions

Is CodePush still available for React Native?

Microsoft retired App Center on March 31, 2025, including the hosted CodePush service. The React Native client SDK (react-native-code-push) is still on npm and the server code is open source, but there is no Microsoft-hosted endpoint to push updates to. Apps still pointing at the old App Center service receive no updates. You either self-host the open-source server or migrate to a different OTA update provider.

What replaced CodePush for React Native?

There is no single replacement. The practical options are: Expo's EAS Update (works with bare React Native through the expo-updates package), self-hosting an open-source CodePush server fork, or one of the smaller hosted services like Pushy or hot-updater. Each has different trade-offs around price, SDK size, rollout controls, and how much native code you have to touch.

Can I use Expo EAS Update without converting to Expo?

Yes. The expo-updates library works in bare React Native apps. You install the package, configure the native side once, and you can push JavaScript bundle updates from EAS or any compatible server. You don't have to adopt Expo's managed workflow or restructure your project to use it.

Are OTA updates allowed by Apple and Google?

Yes, with limits. Apple's App Store Review Guideline 3.3.2 permits updating JavaScript or scripts as long as the change doesn't materially alter the app's purpose or add new features outside what was reviewed. Google Play has similar language. Native code changes still require a store submission. Treat OTA as a way to ship bug fixes, copy changes, and feature flag flips — not new native modules.

How much does an OTA update service for React Native cost?

Hosted plans usually start free for small MAU and scale with monthly active users that download an update. EAS Update has a free tier and per-update pricing above it. Self-hosting is free in licensing but costs engineering time to operate. Budget for the operational side — monitoring, rollback infrastructure, and a runbook for a bad bundle — not just the per-update fee.

Should I roll my own OTA update server?

Only if you have a security or compliance reason that makes hosted services unworkable, and engineering capacity to maintain it. A minimal OTA server is a signed bundle, a manifest endpoint, and a CDN. The hard parts are staged rollouts, version targeting, mandatory updates, rollback, and integrity verification. Most teams underestimate the operational tail and end up rebuilding what EAS Update already does.