Hosting An App on Your Servers

WARNING: This feature is in beta.
Normally, when over-the-air (OTA) updates are enabled, your app will fetch JS bundles and assets from Expo’s CDN. However, there will be situations when you will want to host your JS bundles and assets on your own servers. For example, OTA updates are slow or unusable in countries that have blocked Expo’s CDN providers on AWS and Google Cloud. In these cases, you can host your app on your own servers to better suit your use case.
For simplicity, the rest of this article will refer to hosting an app for the Android platform, but you could swap out Android for iOS at any point and everything would still be true.

First, you’ll need to export all the static files of your app so they can be served from your CDN. To do this, run expo export in your project directory and it will output all your app’s static files to a directory named dist. Asset and bundle files are named by the md5 hash of their content. Your output directory should look something like this now:
├── android-index.json
├── ios-index.json
├── assets
│   └── 1eccbc4c41d49fd81840aef3eaabe862
└── bundles
      ├── android-01ee6e3ab3e8c16a4d926c91808d5320.js
      └── ios-ee8206cc754d3f7aa9123b7f909d94ea.js

Once you've exported your app's static files, you can host the contents on your own server. For example, in your dist output directory, an easy way to host your own files is to push the contents to Github. You can enable Github Pages to make your app available at a base URL like https://username.github.io/project-name.
To host your files locally, follow the instructions below in the 'Loading QR Code/URL in Development' section on setting up a local HTTP server.

In order to configure your standalone binary to pull OTA updates from your server, you’ll need to define the URL where you will host your index.json file. Pass the URL to your hosted index.json file to the expo build command.
For iOS builds, run the following commands from your terminal: expo build:ios --public-url <path-to-ios-index.json>, where the public-url option will be something like https://quinlanj.github.io/self-host/ios-index.json
For Android builds, run the following commands from your terminal: expo build:android --public-url <path-to-android-index.json>, where the public-url option will be something like https://quinlanj.github.io/self-host/android-index.json

You can also load an app hosted on your own servers as a QR code/URL into the Expo mobile client for development purposes.

The URI you’ll use to convert to QR code will be deeplinked using the exps/exp protocol. Both exps and exp deeplink into the mobile app and perform a request using HTTPS and HTTP respectively. You can create your own QR code using an online QR code generator from the input URI.

URI: exps://quinlanj.github.io/self-host/android-index.json
QR code: Generate the URI from a website like https://www.qr-code-generator.com/

Run expo export in dev mode and then start a simple HTTP server in your output directory:
# Find your local IP address with `ipconfig getifaddr en0`
# export static app files
expo export --public-url http://`ipconfig getifaddr en0`:8000 --dev

# cd into your output directory
cd dist

# run a simple http server from output directory
python -m SimpleHTTPServer 8000
URI: exp://192.xxx.xxx.xxx:8000/android-index.json (find your local IP with a command like ipconfig getifaddr en0)
QR code: Generate a QR code using your URI from a website like https://www.qr-code-generator.com/

If you are loading in your app into the expo client by passing in a URL string, you will need to pass in an URL pointing to your json file.
Here is an example URL from a remote server: https://quinlanj.github.io/self-host/android-index.json
Here is an example URL from localhost: http://localhost:8000/android-index.json

When we bundle your app, minification is always enabled. In order to see the original source code of your app for debugging purposes, you can generate source maps. Here is an example workflow:
  1. Run expo export --dump-sourcemap. This will also export your bundle sourcemaps in the bundles directory.
  2. A debug.html file will also be created at the root of your output directory.
  3. In Chrome, open up debug.html and navigate to the Source tab. In the left tab there should be a resource explorer with a red folder containing the reconstructed source code from your bundle.
Debugging Source Code

By default, all assets are hosted from an assets path resolving from your public-url (e.g. https://quinlanj.github.io/self-host/assets). You can override this behavior in the assetUrlOverride field of your android-index.json. All relative URL's will be resolved from the public-url.

Most of the fields in the index.json files are the same as in app.json. Here are some fields that are notable in index.json:
  • revisionId, commitTime, publishedTime: These fields are generated by expo export and used to determine whether or not an OTA update should occur.
  • bundleUrl: This points to the path where the app's bundles are hosted. They are also used to determined whether or not an OTA update should occur.
  • slug: This should not be changed. Your app is namespaced by slug, and changing this field will result in undefined behavior in the Expo SDK components such as Filesystem.
  • assetUrlOverride: The path which assets are hosted from. It is by default ./assets, which is resolved relative to the base public-url value you initially passed in.