Expo eject: how to avoid expo publish

I really hope I missed something.



I can’t run and build my application on iOS without running ´expo publish´. OTA are enabled (unfortunately, because default) but I don’t want to update my running standalone apps. Can this be achieved?


In order to include expo-in-app-purchases I had to eject to bare workflow (SDK 33).

First side note to Expo Team: If you change the wording from ExpoKit to “Bare Workflow” it’s probably a good idea to update the docs accordingly. Otherwise the docs are more confusing than helpful.

Anyway, I got it working and included IAP successfully. Everything was tested in development for iOS. Android is planned to be implemented later.

So I tried to create a build. After having some trouble I found a similar issue where the OP was told to run expo publish before building. So I ran the command and the build succeeded. I think I need to say that I never used expo publish before. What I didn’t know: expo publish updated my running production standalone apps on iOS and Android. As you can imagine the IAP module brings some big changes. So my running applications are now updated (new UI, new logic) but the actual update is not available yet and won’t be available on Android for the next days/weeks. I was able to revert that and to publish the old version again for now. But I have no idea how to build my ios project without publishing.

After following different instructions on Stackoverflow and in the forum I can’t even start the current version in development. XCode always runs the published js bundle. Is it maybe due to the manifestUrl which is set to my published bundle?

Edit: IMHO the ExpoKit docs are not clear regarding OTA as well. It is said:

" ExpoKit : To change the value of enabled , edit ios/<PROJECT-NAME>/Supporting/EXShell.plist and android/app/src/main/java/host/exp/exponent/generated/AppConstants.java . All other properties are set at runtime."

My ExShell.plist does not contain a value “enabled”. So what am I supposed to change there in order to disable OTA? Available keys are isManifestVerificationBypassed set to YES, isShell set to YES, manifestUrl currently set to my local expo since this is the only way I can see changes without publishing and releaseChannel set to default.

1 Like

I can’t run and build my application on iOS without running ´expo publish´. OTA are enabled (unfortunately, because default) but I don’t want to update my running standalone apps. Can this be achieved?

Yes! simply run expo build:ios --no-publish

First side note to Expo Team: If you change the wording from ExpoKit to “Bare Workflow” it’s probably a good idea to update the docs accordingly. Otherwise the docs are more confusing than helpful.

Sorry about the confusion, but bare and Expokit are actually 2 different workflows. It looks like you want to remain using Expokit for now, since you are making use of OTA updates and our builders. In the future, we hope to offer these capabilities to the bare workflow

Thank you for the response. I didn’t know about the --no-publish flag.

Well, that’s the point. I don’t know what to use. I was thinking that Expokit doesn’t exist anymore. But apparently it does. As far as I remember I was asked if I want to eject to bare workflow or pure react native. I have chosen the first one.

Edit: I’ve noticed that expo build:ios won’t work in ExpoKit?! So expo build:ios --no-publish makes no sense. Does it @charliecruzan?

Correct, sorry I must’ve misread the first time and not noticed you were in the bare workflow.

I think I’m still confused, since you should not be able to run expo publish either in the bare workflow, so I’m not sure why you’re saying you can’t build it without doing that. I have a feeling you have an Expokit project, not a bare project as you said. Could you open up your app.json and check if there’s an isDetached key, and what the value of it is?

Also, OTA updates are not available in the bare workflow (they are available in Expokit)

@psas thanks for raising this issue, I’m facing the same issue too with ExpoKit project. Did you manage to find a way to move past this?

@charliecruzan for ExpoKit project, I’m wondering if there is a way to avoid running expo publish when building?

We have OTA enabled, but we want to orchestrate the whole release process of the app because it involves other related services, sending communications to the users, etc. etc.

The problem we are facing now is: running expo publish pushes out an OTA update way too early (while the developer is packaging the app). This could be many days before we should allow the users to see these updates, and certainly may break the app, because other related services are not ready.

Update: I’m considering this approach now. If you also use release channel like me then the below may work for you:

  • Have a different release channel with version number each time we need to push out a “manual update” (as opposed to an “OTA update”). For example, in production users are on production-1.1.0 channel. We want to update to version 1.2.0. This will be done via a new channel production-1.2.0.

  • Consequently, users on the old channel production-1.1.0 will not be able to receive OTA updates. Only users on production-1.2.0 will receive OTA updates moving forward.

  • This makes sense because we assume that the fact that we need to do a “manual update” in version 1.2.0 means that there are some changes that could not be pushed out via OTA update.

  • Backward compatibility needs to be considered as well. Either that, or users who are on version 1.1.0 should be either forced to update manually to version 1.2.0.

To be honest I haven’t really tried to implement this but it makes sense to me for now. Do you foresee any problem with this approach?


I haven’t thought about this as much as you (or @llamaluvr) have, but one thing that might help is publishing to some sort of staging release channel and then promoting it to production when you’re ready for it to be made available to the users.

See also:

Hi @wodin thank you for pointing to these interesting posts. Definitely give me more insights into what we’re trying to do here. I particularly like @llamaluvr solution of using a combination of build number and environment as the release channel, something like prod-105 and prod-109.

Promoting a release to a new channel is also interesting and definitely something I will look into. This could save a tonne of time preparing for releases.

One more piece of the puzzle that I think might interest anyone planning to go down this road is that, once we publish an OTA to the new channel (for example prod-109), we could also publish an OTA to the old channel (for example prod-105) to “disable” it with a message that asks the user to do a manual update. I don’t like having unsupported versions dangling around! Not sure if this is a good practice or there are better ways of doing this though.

If you need to disable the old versions, this is probably the best way to do it. You may go back and forth on how important that is for you (our server-side API will support a few versions back on the app). Generally speaking, if you do show that message, I think you’d just want to make sure as few people see it as possible, by offering a pretty long grace period where the old app will still work.

@llamaluvr thank you, that makes sense. I haven’t tried that before though, so I’m curious to know: when you allow a few compatible versions to exist, does it sort of double or triple the testing efforts? (It’s due to that concern - and also we have a pretty controlled environment of mostly internal users - that I decided to only support the latest version). Thank you so much for sharing the valuable experience.

If we’re making a big server-side API update along with an app update, I’ll regression test the last version of the app against the updated API (in the case of really big changes, we’re also versioning the API, so the updated app is using a different endpoint from the old version). Given your environment, however, I could see disabling the previous version and only enabling the latest to work. I did similar things when I deployed non-Expo apps in internal environments.