skip to content

In EAS Update, what is the difference between a channel and a branch, and how does an installed build decide which update to run?

level: middleimportance: must knowfreq 55%

answer

  1. one is baked in, one is a list
  2. channel travels as expo-channel-name
  3. branch = ordered updates, newest active
  4. channel points at a branch
  5. runtime version and platform must match

basics

~20 s

A channel is a name compiled into a build; a branch is an ordered list of published updates. Each channel points at a branch, and a build runs that branch's newest update matching its runtime version and platform.

solid answer

~40 s

A **channel** is fixed when the binary is built, usually from the build profile's `channel`, and `expo-updates` sends it with every update request as the `expo-channel-name` header; the app can read it as `Updates.channel`. A **branch** lives on EAS servers and is an ordered list of updates, like a Git branch of commits, whose newest entry is the active one. The server links each channel to a branch, by default the branch with the same name, and `eas channel:edit` can repoint it. When a build asks for an update, EAS follows its channel to the branch and serves the newest update whose **runtime version** and **platform** match the build exactly. Because the mapping lives on the server, you can change what production builds receive without a new binary.

code

tsx · 10 lines
tsx
import * as Updates from 'expo-updates';
import { Text } from 'react-native';

export function BuildInfo() {
  return (
    <Text>
      channel: {Updates.channel ?? 'none'} / runtime: {Updates.runtimeVersion ?? 'none'}
    </Text>
  );
}

go deeper

for a junior

Recall that the channel is set when the app is built and the branch is where published updates are stored on EAS.

for a middle

Explain the channel-to-branch pointer, the same-name default, and the exact runtime version and platform match that selects the newest update.

for a senior

Use the indirection deliberately: repoint channels for promotion, and diagnose missing updates by checking channel mapping, runtime version and platform.

for a principal

Design a release model on top of the indirection, such as version-named branches or persistent staging, and decide who may move production's pointer.

## Two layers of an Expo app An app that uses **EAS Update** has two layers. The **native layer** — compiled code, native modules, permissions — is fixed in the binary from the store. The **update layer** — the JavaScript bundle and assets — can be replaced by a compatible update downloaded over the air. Channels and branches are the addressing system that decides which update layer a given binary receives. ## Channel: the name inside the binary A **channel** is a label attached to a build at build time. With EAS Build it normally comes from the `channel` key of the build profile, so a `production` profile yields builds on the `production` channel. The value ends up in the native `expo-updates` configuration as the `expo-channel-name` request header (in `Expo.plist` under `EXUpdatesRequestHeaders` on iOS, in an `AndroidManifest.xml` meta-data entry on Android), and the running app can read it through `Updates.channel` from `expo-updates`. Key properties: - it identifies a **group of builds**, for example all production binaries on both platforms; - it is **baked in**: moving an installed binary to another channel normally takes a new build (experimental runtime overrides exist for preview scenarios); - it says nothing about which code to run — that is the branch's job. ## Branch: the list of updates on the server A **branch** is an object on EAS servers holding an **ordered list of updates**, much as a Git branch holds commits. `eas update --branch <name>` appends a new update; the most recent one is the branch's active update. Branches can be listed with `eas branch:list`, inspected with `eas branch:view`, renamed and deleted, all without touching any binary. ## The link between them Every channel points at a branch. By default a channel is linked to the **branch with the same name**, which is why many teams never notice the distinction: publishing to branch `production` reaches builds on channel `production`. The link is a server-side pointer, so `eas channel:edit production --branch version-2.0` changes what production builds download next without a rebuild. | | Channel | Branch | |---|---|---| | Where it lives | in the binary, sent as `expo-channel-name` | on EAS servers | | What it holds | a name | an ordered list of updates | | How it changes | a new build | `eas update`, rename, delete | | Relationship | points at one branch (or two during a branch rollout) | can be pointed at by any channel | ## How an installed build picks an update When `expo-updates` checks for an update, EAS applies these rules: 1. Follow the build's **channel** to the branch it is linked to. 2. Keep only updates whose **platform** matches the build's platform exactly. 3. Keep only updates whose **runtime version** matches the build's runtime version exactly. 4. Serve the **newest** remaining update on that branch. If nothing matches, the build keeps running what it already has, ultimately the bundle embedded at build time. The runtime-version rule is the safety net that stops JavaScript written for new native code from reaching an old binary. ## Inspecting the mapping When updates do not arrive where expected, the state is all inspectable from EAS CLI: - `eas channel:list` and `eas channel:view production` show each channel and the branch it points at; - `eas branch:list` and `eas branch:view production` show a branch's updates, newest first, with their messages and runtime versions; - `eas update:view <group-id>` shows one update group per platform. Renaming a branch with `eas branch:rename` keeps existing channel links, so a channel follows the renamed branch. On the device, logging `Updates.channel` and `Updates.runtimeVersion` confirms which side of the match a build is on. ## Why the split is useful Separating "which builds" (channel) from "which updates" (branch) lets a team keep builds stable while moving update streams around them. A staging binary and a production binary can have identical native code but different channels; an update stream can be tested on one and then served to the other by repointing a channel or republishing the tested update. Deployment patterns such as version-named branches (`version-1.0`, `version-2.0`) rely on exactly this indirection.

  • You publish with eas update --branch hotfix, but no user receives it; why?
    Updates reach builds only through a channel. If no channel is linked to `hotfix`, no build follows it. Link a channel with `eas channel:edit production --branch hotfix`, or publish with `--channel production`, which writes to whatever branch production points at.
  • Why does an update on the right branch still not reach some production users?
    Matching also requires the same runtime version and platform. Users on an older binary with a different runtime version keep their current bundle, and an update published only for Android never reaches iOS builds.

A channel is the station a radio was tuned to at the factory; a branch is a playlist on the broadcaster's side. The broadcaster can switch which playlist that station plays, and every radio tuned to it hears the change, as long as the song is in a format the radio can play.

saying these in an interview costs you the question

  • A channel is a server-side list of updates
  • Branches are compiled into the binary at build time
  • An update reaches any build on the channel whatever its runtime version
  • Changing which branch a channel follows requires a new store build
  • Channels and branches are the same thing with two names