Migrate from App Center CodePush

Plan a migration from Microsoft App Center CodePush to Otalan, with mapping notes, rollout guidance, and common failure modes.

Microsoft announced Visual Studio App Center retirement for March 31, 2025. Microsoft later extended Analytics and Diagnostics support, but states that the remaining App Center features retired after March 31, 2025. For CodePush, Microsoft points teams to a standalone CodePush server that can run independently from App Center.

Sources:

This page explains how to think about a migration from App Center CodePush to Otalan. It is not an automatic import tool.

Mental model mapping

App Center CodePush conceptOtalan concept
App Center appOtalan project app, usually the same mobile appId
Deployment, such as Staging or ProductionOtalan channel
Deployment key in the installed appOTA App Key plus exact app/channel/runtime settings
CodePush release packageOtalan bundle
Target binary versionruntimeVersion compatibility line
Promote releasePublish or roll out the same tested tuple/channel intentionally
RollbackReactivate an older Otalan bundle in the same tuple
CLI release automation@otalan/cli bundle and publish flow

The biggest difference is tuple strictness. Otalan always matches appId, platform, channel, and runtimeVersion exactly before serving an update.

Migration plan

  1. Inventory your CodePush apps, deployments, deployment keys, rollout rules, and target binary versions.
  2. Decide the Otalan appId, platform, channel, and runtimeVersion mapping for each active release lane.
  3. Create the Otalan organization, project, app registration, OTA App Key, and OTA Publish Key.
  4. Add the Otalan runtime integration for the app stack: @otalan/capacitor for Capacitor or @otalan/expo with expo-updates for Expo.
  5. Ship a new native binary that points to Otalan. Existing binaries that still point to CodePush will keep asking CodePush until users update the app.
  6. Configure CI with the OTA Publish Key and the same tuple values used by the runtime.
  7. Publish a canary bundle with otalan bundle and otalan publish.
  8. Verify the release path: publish one update, publish a second update, roll back, then confirm startup on the target devices.
  9. Freeze new CodePush publishes only after the Otalan-backed binary is available and verified.
  10. Keep old CodePush infrastructure or a compatibility plan for users who have not yet upgraded from old binaries.

Do not assume historical CodePush releases are imported into Otalan. Publish the known-good current bundle into Otalan and use that as the first rollback anchor.

Runtime integration notes

For Capacitor apps:

  • install @otalan/capacitor
  • keep @capawesome/capacitor-live-update, @capacitor/app, and @capacitor/core aligned with the supported Capacitor range
  • put only the OTA App Key in the app
  • keep OTA Publish Keys in local release tooling or CI

For Expo apps:

  • install @otalan/expo
  • keep expo-updates enabled
  • point the Expo update URL to the Otalan manifest endpoint
  • include the OTA App Key in update request headers
  • initialize @otalan/expo once and call updater.check() for updateAvailable or updater.sync() for check-and-apply work
  • keep checkAutomatically controlled so Expo does not check before the Otalan helper is initialized

CI publishing pattern

A typical first Otalan CI job looks like this:

bun add -g @otalan/cli
otalan login --api-url "$OTALAN_API_URL" --api-key "$OTALAN_API_KEY"
otalan init --app-id "$OTALAN_APP_ID"
bun run build
otalan bundle --target capacitor --platform ios --runtime-version "$OTALAN_RUNTIME_VERSION"
otalan publish --channel "$OTALAN_CHANNEL" --release-notes "$GIT_COMMIT"

Use --target expo for Expo lanes, and publish iOS and Android separately when their generated assets differ.

Rollback flow

Rollback is server-side state change inside the same tuple:

  1. List bundles for the tuple with the dashboard or otalan bundles.
  2. Pick the last known-good bundle.
  3. Roll back with the dashboard or otalan rollback.
  4. Restart or re-check the app and confirm it receives the rolled-back bundle.

Rollback cannot switch native binary compatibility. If the broken release needs a different native plugin, permission, or runtime version, ship a store update and use a new runtimeVersion.

Common migration failure modes

  • old installed binaries still point to CodePush because no new store binary was shipped
  • CodePush deployment names were mapped to the wrong Otalan channels
  • runtimeVersion was treated like a label instead of a native compatibility boundary
  • iOS and Android were published into the same lane even though their exported assets differ
  • the app shipped an OTA Publish Key instead of an OTA App Key
  • Expo update requests bypassed updater.check() or updater.sync() and ran before the Otalan helper initialized
  • dashboard manual Expo upload omitted .otalan/bundle/manifest.json

Treat the migration as a release-platform change, not just a package rename.