> For the complete documentation index, see [llms.txt](https://mercure-technologies.gitbook.io/expo-open-ota/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mercure-technologies.gitbook.io/expo-open-ota/eoas/progressive-rollouts.md).

# Progressive rollouts

{% hint style="warning" %}
Progressive rollouts require [control-plane mode](/expo-open-ota/controle-plane-mode/overview.md): rollout state lives in Postgres. In stateless mode the `--rollout-percentage` flag is rejected with an explicit error and the dashboard shows no rollout controls. They are part of the open-source core, so no Enterprise license is needed.
{% endhint %}

Progressive rollouts let you ship a change to a fraction of your devices first, watch how it behaves, and only then widen it to everyone. Expo Open OTA supports the two rollout mechanisms you may know from EAS Update: staging a single update inside a branch, and splitting a channel between two branches.

### How devices are assigned

Every device running expo-updates sends a stable identifier with each update check: the `EAS-Client-ID` header, a UUID generated on first launch and persisted for the lifetime of the installation.&#x20;

The server hashes this identifier together with a salt that is unique to each rollout, which produces a number between 0 and 1, and a device is part of the rollout when that number falls below the configured percentage.

This design has a few properties worth understanding:

* Assignment is deterministic and requires no per-device state on the server. The same device always lands in the same bucket for a given rollout.
* Increasing the percentage only ever adds devices. A device that received the update at 10% is still in the rollout at 25%, so nobody flip-flops between versions as you progress.
* Each rollout uses its own salt, so the devices that go first are reshuffled from one rollout to the next. The same early adopters do not absorb every risky release.
* A device that does not send the header, such as a very old expo-updates version or a custom client, is never part of a rollout and keeps receiving the default content.

### 1. Per-update rollouts

A per-update rollout stages a single update inside a branch. You start one by publishing with the `--rollout-percentage` flag:

```bash
npx eoas publish --branch production --rollout-percentage 10
```

Devices in the rollout receive the new update. Every other device keeps receiving the update that was current at the moment you published, which the dashboard calls the control. If you publish for both platforms at once, each platform gets its own rollout at the same percentage.

{% hint style="info" %}
If you publish through an npm script, remember the argument separator: `npm run release -- --rollout-percentage 10`. Without the `--`, npm swallows the flag and a plain publish goes out to everyone.
{% endhint %}

While the rollout is active, the branch is locked for that runtime version: publishing another update, rolling back, or republishing returns a conflict until you finish or revert the rollout. This mirrors EAS Update and prevents you from accidentally stacking a release on top of an unfinished rollout.

#### Managing it from the dashboard

Open Branches & updates, select the branch and then the runtime version, and the rollout card appears above the updates table. From there an admin can:

* Increase the percentage. Progression is increase-only: devices that already installed the update keep it, so lowering the number would not actually pull the update back from anyone.
* Finish the rollout, which delivers the update to all devices and unlocks publishing on the branch.
* Revert the rollout. The control update is republished as a new update, so every device converges back on the previous content. Devices that had installed the rolled-out update download the republished control at their next check.

The updates table also shows the state at a glance: the rolled-out update carries a percentage badge, and the control update is tagged so you can see what the rest of your fleet is receiving.

If you start a rollout on a branch that had no previous update, there is no control: devices outside the rollout simply receive nothing yet, and reverting creates a rollback to the embedded update.

### 2. Channel rollouts

A channel rollout splits one channel between two branches: the channel keeps serving its mapped branch to most devices while a percentage receives another branch. It is the right tool when the change you want to stage spans several updates, or when you want to compare two lines of development.

Channel rollouts are managed entirely from the dashboard. On the Channels page, the rollout column of each mapped channel offers Start rollout: you pick the branch to roll out, which must differ from the channel's current branch, and the starting percentage. While a rollout is active:

* the channel's branch mapping is locked, since remapping it would change the meaning of the split; you end the rollout to change it,
* you can adjust the percentage in either direction from the Manage dialog,
* publishing to both branches remains possible, exactly like on EAS.

A device whose runtime version has no update on the rollout branch falls back to the default branch, so shipping a rollout branch that only covers your newest runtime version is safe.

Ending a channel rollout is a choice between two outcomes. Promote points the channel at the rollout branch for everyone: it becomes the channel's branch and the rollout is over. Revert discards the split and returns every device to the default branch. In both cases devices apply the change at their next update check, so allow a restart or two before the whole fleet converges.

### Requirements and limits

* Percentages go from 1 to 99. A rollout at 100% does not exist as a state: finishing the rollout is how you reach everyone.
* One rollout at a time: a channel can only run one branch split, and a branch can only stage one update per runtime version.
* Publishing with `--rollout-percentage` goes through the same API-key checks as a normal publish, so [protected branches](/expo-open-ota/security/branch-protection.md) and [IP restrictions](/expo-open-ota/security/ip-whitelisting.md) keep applying.
* The `EAS-Client-ID` header can be spoofed by a modified client, so treat rollout membership as a distribution mechanism, not a security boundary.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mercure-technologies.gitbook.io/expo-open-ota/eoas/progressive-rollouts.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
