> 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/storage/overview.md).

# Overview

**Expo Open OTA** supports multiple storage solutions for hosting your update assets: **Amazon S3**, **Google Cloud Storage (GCS)**, **Azure Blob Storage**, **S3 Compatible Storage like Cloudflare R2** and **Local File System**. This guide will help you set up your storage solution and configure your server to use it.

### Storage layout

Expo Open OTA stores each update under its application, branch, and runtime version.

```
appId/
└── branch/
    └── runtimeVersions/
        └── update/
```

This hierarchy isolates updates across applications, branches, and runtime versions.

### Object key prefix

S3, S3-compatible storage, GCS, and Azure Blob Storage support an optional key prefix. Set `BUCKET_KEY_PREFIX` to store all Expo Open OTA objects below that prefix.

For example, `BUCKET_KEY_PREFIX=expo-open-ota` stores objects under `expo-open-ota/appId/...`.

Local Storage honors `BUCKET_KEY_PREFIX` too — objects are stored under `{LOCAL_BUCKET_BASE_PATH}/{prefix}/…`. Do not also include the prefix in the configured local storage path, or it is applied twice.

### Storage migrations

Expo Open OTA runs a migration job against the configured storage. The job applies required storage changes and records each completed migration.

Migration state is stored in `.migrationhistory`. Do not delete or modify this file. It prevents completed migrations from running again.

### Inside an update directory

EOAS uploads the output of `expo export`: the JS bundles under `_expo/`, the `assets/` directory, `metadata.json`, and `expoConfig.json`.

The server adds its own files on top:

| File                   | Written                         | Purpose                                                                                                                                                                | Mode      |
| ---------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| `update-metadata.json` | At publish time                 | Holds the platform, the git commit hash, and the optional update message. The update UUID — the `id` served in the expo manifest — is written into it at publish time. | Stateless |
| `.check`               | Last, after upload verification | Marks the update as complete. An update without it is invisible to clients and the dashboard.                                                                          | Stateless |
| `rollback`             | By `eoas rollback`              | Marks the directory as a rollback. The server serves it as a `rollBackToEmbedded` directive instead of a manifest.                                                     | Stateless |

These files are internal to the server. `update-metadata.json` and `.check` are excluded from asset serving. Do not edit or delete them: without `update-metadata.json` the update loses its identity, and deleting `.check` hides a published update.

A rollback is stored as a regular update directory containing only the server files — no bundle, no assets. It participates in update resolution like any other update: publishing after a rollback resumes normal delivery.

#### Update IDs

Each update directory is named after its publish timestamp in milliseconds. iOS and Android publish separate directories; a platform digit is appended to the timestamp so simultaneous publishes never collide. The UUID shown in the dashboard is the manifest `id`, read from `update-metadata.json`, or derived deterministically from the export metadata for updates published before the UUID was stored.

#### Control-plane mode

In control-plane mode this bookkeeping moves to the database: the `updates` table holds in columns what `update-metadata.json` holds in a file, and a `checked_at` column replaces the `.check` sentinel. Storage keeps only the bundles and assets. The marker files are a stateless-mode mechanism, but bucket migrations run at startup in every mode — so `.migrationhistory` appears in control-plane deployments too.

### Stateless mode

In stateless mode, storage is the source of truth for updates. The server reads the storage hierarchy to:

* Build manifests for update clients.
* List updates for the dashboard.
* Resolve the update for an app, branch, and runtime version.

The server also uses a cache to avoid repeated storage lookups. Configure caching to improve performance as update history grows. See [Caching overview](/expo-open-ota/caching/overview.md).


---

# 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/storage/overview.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.
