skip to content

In EAS Build, what is the difference between the remote and local cli.appVersionSource in eas.json, and how does autoIncrement behave under each?

level: middleimportance: must knowfreq 50%

answer

  1. who owns the build number
  2. remote: counter on EAS servers
  3. local: CLI edits app.json
  4. true bumps versionCode / buildNumber
  5. "version" bumps patch, local only

basics

~20 s

With remote, EAS servers store versionCode and buildNumber and inject them at build time; autoIncrement bumps the server counter. With local, the project files are the truth and autoIncrement edits app.json, which you must commit.

solid answer

~40 s

`cli.appVersionSource` decides who owns the **developer-facing build version** (`android.versionCode`, `ios.buildNumber`). With `"remote"`, the recommended setting since EAS CLI 12, EAS servers keep the value per app identifier; `autoIncrement: true` on a build profile bumps it each time a build is requested, and the value is injected into the native project during the build, so app config values are ignored. With `"local"`, EAS reads the versions from your project; `autoIncrement` makes the CLI edit **app.json** (and the native files) before the build, and the change persists only if you commit it. `autoIncrement` also accepts `"version"`, which bumps the user-facing `version` by a patch step, but only with a local source; remote rejects it. The store-visible `version` stays yours to set either way.

code

json · 13 lines
json
{
  "cli": {
    "appVersionSource": "remote"
  },
  "build": {
    "preview": {
      "distribution": "internal"
    },
    "production": {
      "autoIncrement": true
    }
  }
}

go deeper

for a junior

Recall the two numbers, user-facing version and build version, and that remote keeps the build version on EAS servers while local keeps it in your project.

for a middle

Explain what autoIncrement does under each source: a server-side bump injected at build time versus an edit to app.json that must be committed.

for a senior

Show you can predict the failure modes: lost CI bumps under local, ignored app config values under remote, and the nativeVersion runtime policy conflict.

for a principal

Argue where the version counter should live for a team with many builders, and how versioning ties into update compatibility policies.

## Two version numbers, two owners Every mobile app carries two versions: | Value | Expo app config | Android | iOS | Who sees it | |---|---|---|---|---| | user-facing version | `version` | `versionName` | `CFBundleShortVersionString` | store listing, users | | build version | `android.versionCode` / `ios.buildNumber` | `versionCode` | `CFBundleVersion` | the stores, developers | The stores reject an upload whose **build version** repeats one they already have, so it must go up on every uploaded build. The user-facing `version` changes only when you start a new release cycle. EAS's versioning settings are about automating the first without touching the second. ## cli.appVersionSource `appVersionSource` sits under the top-level `cli` key of `eas.json` and takes `"remote"` or `"local"`. - **`remote`** — EAS servers store the current `versionCode` and `buildNumber` for each application identifier. At build time the stored value is written into the native project, which makes the server the **source of truth**. `android.versionCode` and `ios.buildNumber` in app config are ignored; EAS CLI warns that they are still copied into the manifest that `expo-constants` exposes and recommends deleting them. Expo documents `remote` as the recommended behaviour from EAS CLI 12.0.0. - **`local`** — EAS reads the versions from your project as they are and builds with them. It writes to the project only when `autoIncrement` asks it to. If the field is missing, EAS CLI warns that it will be required. With `autoIncrement: true` an interactive run asks you to choose, and a `--non-interactive` run proceeds with `local` without saving the choice. ## autoIncrement under each source `autoIncrement` is a **build profile** key, so it can be on for `production` and off for `preview`. 1. **`true`** increments the build version: `versionCode` on Android, `buildNumber` on iOS. 2. **`"versionCode"`** (Android) or **`"buildNumber"`** (iOS) says the same thing explicitly for one platform. 3. **`"version"`** bumps the user-facing `version` by one patch step (1.4.2 to 1.4.3), and is **rejected with a remote source**. With **remote**, incrementing is a server operation. When you request a build, EAS CLI reads the latest stored value, stores the next one and injects it during the build. Nothing in your repository changes, so every machine and every CI run shares one counter. If the server has no value yet, it is seeded from the local project; if EAS CLI cannot read one there either, the build stops and asks you to run `eas build:version:set`. With **local**, incrementing is a file edit. EAS CLI bumps the number in **app.json** and synchronises the native files, so it needs a static `app.json`; a project with only `app.config.js` fails with "autoIncrement option is not supported when using app.config.js". The edit lands in the working copy of whoever ran the build. On a laptop you commit it; in CI it is thrown away with the checkout, which is how duplicate build numbers happen. ## Choosing between them For most teams the choice follows from **who builds**: - One developer building from one laptop can live with `local`: the bump lands in app.json and is committed with the release. - Several people, or any CI pipeline, want `remote`: the counter lives in one place, no bump commits are needed, and two builders cannot hand out the same number from stale checkouts. - A bare project whose native build files EAS cannot parse, such as multi-flavour Gradle setups, may have to keep `local` and manage numbers by hand. Whichever you pick, set it explicitly. Leaving `appVersionSource` out produces a warning on every build and, in CI, a silent fallback to `local`. ## Commands that go with remote versions - `eas build:version:set` — set the stored value, for example to the last number the store accepted. - `eas build:version:get` — print the stored values. - `eas build:version:sync` — write the stored values into the local project, for building in Xcode or Android Studio. ## Interactions to remember - A remote source is incompatible with the `nativeVersion` runtime version policy of EAS Update; the CLI tells you to switch to `appVersion`. - Read versions at runtime with `expo-application` (`nativeBuildVersion`), not from app config, because with remote the config no longer holds the real build number. - The **user-facing `version`** is never automated by the remote source: raise it yourself when a release cycle begins. - Multi-flavour bare Android projects are a documented limitation: local `autoIncrement` cannot edit them, and `eas build:version:sync` does not support them.

  • Why does autoIncrement with a local version source cause duplicate build numbers on CI?
    With `local`, `autoIncrement` edits app.json in the working copy. A CI job builds and then discards its checkout, so the bump is never committed and the next job reads the old value again. Either commit the change back, which is awkward to coordinate, or switch to `"remote"` so the counter lives on EAS servers.
  • What happens if you set autoIncrement to "version" with appVersionSource set to remote?
    EAS CLI refuses the build with an error that `{"autoIncrement": "version"}` is not supported when the app version source is remote. The remote source manages only the build version; the user-facing `version` is edited by hand in app config.
  • How do you build locally in Xcode with the same build number EAS stores remotely?
    Run `eas build:version:sync`. It writes the stored `versionCode` and `buildNumber` into the local project, so a build made outside EAS uses the same numbers the remote source would have injected.

The remote source is a ticket dispenser at a deli counter: every builder pulls the next number from one machine. The local source is each clerk writing numbers on a notepad; unless the notepad is shared, two clerks hand out the same number.

saying these in an interview costs you the question

  • Remote versioning rewrites app.json and commits the new build number
  • autoIncrement true bumps the store-visible version string
  • autoIncrement "version" works with the remote version source
  • With remote, ios.buildNumber in app config still decides the build number
  • A local autoIncrement bump in CI persists to the next build automatically