EAS Update OTA Architecture for Managed Expo Apps
Architecture note for using EAS Update to deliver JavaScript and assets over the air in managed Expo apps, covering requirements, minimal design, trust boundaries, operational checks, failure modes and when to change the design.
10 Jul 2025, 16:15 UTC

The problem with OTA in practice
You need to ship JavaScript fixes and asset changes faster than an app store review cycle, without changing native code. EAS Update can deliver JS bundles and assets over the air, but only if the native runtime in the store binary already supports the code you want to run. The useful takeaway is to treat the store binary as immutable and the JS bundle as a separately versioned artifact gated by channels, with explicit checks for runtime compatibility.
Requirements for an OTA-only update path
A managed or prebuild Expo project with an EAS project linked is required. The app must be configured with updates enabled and an update channel assigned. The update path is viable only when the change does not require native code, entitlements, permissions, or new native modules.
- Project linked: EAS project slug present in local configuration and accessible with an EAS access token.
- Update enabled: app configuration declares updates with an update URL and channel.
- Channel configured: at least one channel, typically internal and production, defined for rollout control.
- No native dependency: the JS change must run on the native runtime shipped in the current store binary.
Example app configuration snippet for a managed app:
{
"expo": {
"name": "MyApp",
"slug": "myapp",
"extra": {},
"updates": {
"enabled": true,
"channel": "production",
"url": "https://u.expo.dev/<project-id>"
}
}
}Replace <project-id> with the Expo project identifier. The flag enabled and channel name are the primary configuration points to inspect.
Smallest suitable design
Keep the native binary stable and publish JS bundles via EAS CLI to a named channel. The client checks for updates on app start and applies them in the background.
Configuration
Enable updates in app.json or app.config.js and set a default channel. Use a separate channel for internal testing.
Publish flow
From a development machine with EAS CLI installed and authenticated, publish an update to a channel. Required permissions are an EAS token with write access to the project.
eas update --channel production --message "Fix checkout validation"The command builds a JS bundle from the current working tree, uploads it to Expo hosted update infrastructure, and creates a manifest pointing to that bundle. Meaningful risk: publishing from a dirty working tree can ship unintended code. Verify the commit and working tree before publishing.
Client behavior
The client authenticates to the update service using the app bundle identifier and Expo generated credentials embedded at build time. On start, the client compares the current runtime version and manifest with the channel, downloads if newer, and installs on next launch. The design keeps native binaries immutable between updates.
Trust and data boundaries
The update payload lives on Expo hosted infrastructure. The trust boundary includes Expo as a delivery intermediary between your build machine and the device.
- Client to Expo Update service: device identifies via bundle identifier and app slug. No user secrets are transmitted for update checks.
- Payload scope: only JavaScript, assets, and the update manifest are mutable via OTA. Secrets, native modules, entitlements and app permissions remain in the signed binary.
- Data boundary: update manifests contain URLs to bundles and a checksum. Sensitive data should not be embedded in JS bundles that are delivered OTA.
This limits blast radius but requires trust in Expo for availability and integrity of delivery. Data residency and supply chain policies may restrict third-party OTA for regulated user segments.
Operational checks
Verification should be explicit and repeatable.
- Configuration check: inspect app config for updates.enabled true and a defined channel. Confirm EAS project linkage by checking project metadata in local configuration.
- Channel membership: confirm which channel a build is subscribed to and that internal testers are on a non-production channel.
- Size and compatibility: validate bundle size against OTA limits for the SDK in use and ensure the JS bundle targets the native runtime version shipped in the store binary. SDK behavior and limits are version-sensitive.
- Health signals: emit client telemetry after update check, download success, and install success. Track update success rate and install errors per channel.
- Rollback: publishing a previous manifest to the channel can revert clients to an earlier JS bundle. Rollback is possible because the manifest is mutable.
A practical way to check the result is to trigger a test update on an internal channel and observe the client update check, download and install flow on a physical device, confirming channel gating and that only JS and assets changed.
Failure modes and mitigations
Network unavailability causes deferred updates. The client should retry on next launch and not block startup.
Version skew occurs when JS expects a native module or API not present in the shipped binary. Mitigation is to pin the minimum native runtime version in the JS bundle and enforce it via a remote config kill-switch that forces a minimum native version.
A bad manifest can cause startup failure. Mitigations are staged rollouts to a small percentage of users, channel pinning for critical users, and the ability to publish a known-good manifest to the channel.
Conditions that change the design
Move away from OTA-only when any of the following hold:
- Native code change is required, including new native modules, entitlements, or permission changes.
- Use of unsupported native modules that are not compatible with managed workflow OTA constraints.
- Compliance requirements prohibit third-party OTA delivery or require data residency controls not offered by Expo hosted updates.
In those cases the design changes to full store releases or a self-hosted update infrastructure with its own trust and operational boundaries.
Limitations: OTA updates cannot change native code, entitlements, or app permissions. Treating EAS Update as a replacement for store releases leads to design failures when native changes are required. Assumptions about size limits and manifest schema should be revalidated for the SDK and EAS release in use.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.