CodePush is retired. What to use instead.

Microsoft retired Visual Studio App Center — and CodePush with it — on 31 March 2025. On 20 May 2025 both repositories were archived and made read-only: microsoft/react-native-code-push, the client library, and microsoft/code-push-server, the standalone server Microsoft published so teams could keep running it themselves.

That server's README is explicit about what you are taking on: "This code is provided 'as is', because of that Microsoft will not provide support services for it."

So the question is no longer whether to move. It is what to move to, and how much of your app that costs.

The four honest options

1. Keep CodePush, run it yourself

Deploy code-push-server, point the CLI at it, change nothing in your app. It is the smallest change today and the largest liability tomorrow: an archived server and an archived client SDK, neither receiving fixes, sitting between your users and every release you ship. When React Native's next architecture change breaks the native module, nobody upstream is going to fix it.

Reasonable if you need weeks, not years, and you are already migrating elsewhere.

2. EAS Update

Expo's own service, and the reference implementation of the protocol described below. Technically it is the safest destination: it is maintained by the people who maintain the client. It requires an Expo account, and its billing runs through Expo — which, if you are paying from Russia, is where the conversation usually ends.

3. A hosted service with its own SDK

Several products appeared to fill the CodePush gap, each shipping its own React Native SDK. They work. But you would be leaving one proprietary client library for another, which is precisely the position that made March 2025 expensive. If that vendor stops, you migrate again.

4. A service on the open Expo Updates protocol

expo-updates is a public client library implementing a published wire protocol. Any server that speaks it can serve your app, and switching between such servers is a URL in your config — not an SDK swap. Otapush is one of those servers. So is EAS Update. That is deliberate: the two arguments below are the only ones we can make that a competitor cannot simply copy.

Comparison

Self-hosted CodePush EAS Update Otapush
Client library react-native-code-push (archived) expo-updates expo-updates
Who maintains the client nobody Expo Expo
Who runs the server you Expo we do
Protocol CodePush, proprietary Expo Updates v1 Expo Updates v1
Expo account required no yes no
Cost of leaving rewrite the client one URL one URL
Payment in rubles, invoice for companies your hosting bill no yes

The question that actually decides it

Not features. Who owns the library inside your binary.

A proprietary update SDK is the one dependency you cannot swap without a store release, because it is compiled into the app your users already installed. When CodePush shut down, that is what made the migration expensive: not the server, the client.

expo-updates is the only client in this space that is not owned by an update vendor. Choosing a server that speaks its protocol means your next migration — including away from us — is a configuration change.

Migrating from react-native-code-push

The work is replacing the client library. The server side is a URL.

1. Remove CodePush

Unlink and uninstall react-native-code-push, and remove the codePush() wrapper from your root component along with its native configuration (CodePushDeploymentKey in Info.plist, the CodePush entries in strings.xml and MainApplication).

2. Install expo-updates

In a bare React Native project:

npx install-expo-modules@latest
npm install expo-updates
cd ios && pod install

install-expo-modules adds the Expo modules core that expo-updates needs. This does not turn your project into a managed Expo app — see Bare React Native for what it does and does not change.

3. Point it at your app

npm install -g otapush
otapush login
otapush init

otapush init writes the update URL, the runtime version and your app's code-signing certificate into AndroidManifest.xml and Expo.plist (or app.json for managed Expo projects). Expo setup covers the managed case.

4. Rebuild once, then publish

The update URL and the signing certificate live in the binary, so this migration needs one store release. After that, publishing is:

otapush publish --channel prod --platform ios

What changes in your JavaScript

CodePush's codePush.sync() did checking, downloading and restarting in one call. expo-updates splits them: Updates.checkForUpdateAsync(), Updates.fetchUpdateAsync(), Updates.reloadAsync(), with the useUpdates() hook if you want to drive UI from update state. By default the library checks on launch and applies on next launch, which is the behaviour most CodePush deployments were configured for anyway.

Deployment keys become channels. Where CodePush had Staging and Production deployments, Otapush apps come with dev, staging and prod, and promoting a verified build between them is one command — see the CLI reference.

Paying from Russia

This is the reason most of the teams that write to us are writing.

Expo bills through international payment providers. Otapush takes cards and SBP in rubles through Russian acquiring, and companies can pay by invoice against a Russian legal entity. Prices are on the pricing section, metering is by MAU — unique devices that check for or download an update in a calendar month — and going over the limit does not stop updates being served.

If you are outside Russia, self-serve payment is not available yet; write to us and we will arrange it.

Questions

Do I have to adopt Expo to use expo-updates?

No. expo-updates runs in a bare React Native project. It needs the Expo modules core, which install-expo-modules adds, but your project keeps its own native build, its own CI and its own release process. No Expo account is involved.

Can I keep using CodePush and decide later?

Yes, and for a short window that is a defensible choice. What you are betting on is that nothing in React Native breaks an unmaintained native module before you migrate. That bet gets worse every release.

Is this allowed by the App Store?

Yes, for JavaScript and asset updates. Apple's Developer Program License Agreement §3.3.1(B) permits downloading interpreted code as long as it does not change the app's advertised purpose or bypass OS security; Google Play carries the same exception for interpreted languages. Native changes still require a store release. App store policies quotes both in full.

What happens if your server goes down?

expo-updates launches the bundle already on the device — the last update it downloaded, or the one shipped in the binary — and retries on the next launch. An outage of the update server cannot take your app down. This is a property of the client, and it was true of CodePush too.

How is this different from the service with its own SDK I was about to pick?

Mechanically, not much: both deliver a JavaScript bundle. The difference shows up the day you want to leave. Ours costs a URL because the client is not ours.

Ship your first OTA update today

The free plan includes 10 MAU — unique devices per month. No card required.