Expo has launched Expo Simulators, a hosted service that starts an iOS Simulator or Android Emulator in the cloud on request. The pitch is aimed at coding agents. Claude Code, Codex, Cursor or a CI job can start a device, install your build, tap through the app and come back with a screen recording, screenshots and logs that show the change works. It's generally available on all paid Expo plans as of November 12, and it doesn't need a Mac.
If you build React Native apps with Expo and let agents touch your UI code, this matters. If you don't use agents or EAS, you can skip it for now.
What actually shipped
Each session is one remote device running in its own VM on Expo's infrastructure. You can start one from EAS CLI, a REST API, a "create session" link, or the project's Simulator sessions page on expo.dev. The agent controls the device with tools it may already use locally. The blog lists agent-device, Argent, or Appium, and you choose one with --type. A web-preview-only session type gives a human the live browser view without exposing the agent controller.
The recording features are the part that's genuinely new. Every session records video, screenshots, logs, network traffic and CPU and memory usage, and the session page stays available after the device shuts down. The announcement states the goal plainly:
"Instead of 500 lines that just look about right, the reviewer gets a recording that shows the change works."
(Szymon Dziedzic, Expo Blog)
Expo's example is an agent that ran an app at the largest accessibility text size, found overlapping labels, fixed the layout and attached before and after screenshots to the PR.
This isn't the first public appearance of the service. Unite.AI reports that it follows a limited-access EAS Simulator preview from September 14, 2026. Expo says design partners have been using it for several months. This launch removes the waitlist.
Starting a session
According to the start-a-session docs, the entry point is eas simulator:start, run from a project directory:
1eas simulator:start --platform ios --type agent-device \
2 --name "Checkout flow screenshots" --non-interactiveThe command waits in the queue if needed, gives the device up to 15 minutes to boot, and then writes connection details to .env.eas-simulator. The flags you'll use most:
- --build-id or --build-fingerprint installs a matching simulator build from EAS Build. --application-archive-url installs a build from any URL, so the build doesn't have to come from EAS.
- --expo-go installs the Expo Go version that matches your project's SDK.
- --open-url opens an Expo or development build URL once the app launches.
- --max-duration-minutes and --max-idle-time-minutes stop the session automatically.
- --tag groups sessions. --egress local routes device traffic through your machine.
Before you script against a session, check that it's live with eas simulator:get --json --non-interactive and wait until status is IN_PROGRESS and remoteConfig is present. The docs warn that the .env file alone doesn't prove the device is running, because it can hold the ID of a session that has already stopped.
The local loop, before and after
Right now an agent that checks its own UI work probably runs npx expo start and drives a simulator on your laptop. With Expo Simulators, the Metro server stays local and the device moves to the cloud. The docs describe exposing Metro through a tunnel and pointing the remote dev build or Expo Go at it, and Fast Refresh keeps working:
1# Before: local Metro, local simulator
2npx expo start --ios
3
4# After: local Metro over a tunnel, cloud simulator
5EXPO_UNSTABLE_TUNNEL_V2=1 npx expo start --tunnel
6eas simulator:start --platform ios --type agent-device --expo-go \
7 --open-url "<your tunneled exp:// URL>" \
8 --max-idle-time-minutes 15 --non-interactiveLook at the UNSTABLE in that environment variable. The tunnel is how Expo recommends iterating against a cloud device, but it's flagged as unstable, so expect it to change.
Advertisement
The simplest setup is to install Expo's eas-simulator skill in your agent and let it check access, start the session, drive the app, collect evidence and stop the session. If your agent already reads skills, start there.
Wiring it into CI
The Run in CI guide says to authenticate with a robot user that has at least the Developer role and to store its token as EXPO_TOKEN, because eas login doesn't work in CI. The guide gives shell steps. Here they are in a GitHub Actions job, with the stop step set to run even when earlier steps fail:
1name: simulator-smoke
2on: pull_request
3
4jobs:
5 smoke:
6 runs-on: ubuntu-latest
7 env:
8 EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
9 steps:
10 - uses: actions/checkout@v4
11 - uses: actions/setup-node@v4
12 with:
13 node-version: 20
14 - name: Check Expo Simulators access
15 run: npx --yes eas-cli@latest simulator:availability --json | jq -e '.available'
16 - name: Start iOS session
17 run: |
18 npx --yes eas-cli@latest simulator:start --platform ios --type agent-device \
19 --build-fingerprint "<fingerprint-hash>" \
20 --name "PR ${{ github.event.number }} smoke test" --tag ci \
21 --max-duration-minutes 30 --non-interactive
22 - name: Open app and screenshot
23 run: |
24 npx --yes eas-cli@latest simulator:exec npx agent-device@latest open com.example.app --platform ios
25 npx --yes eas-cli@latest simulator:exec npx agent-device@latest screenshot ./artifacts/home.png
26 - name: Stop session
27 if: always()
28 run: npx --yes eas-cli@latest simulator:stopThe jq -e step is there because simulator:availability exits 0 even when the account doesn't have access. Without it, the job would only fail later. Also note that the CI machine is Ubuntu, and it can still test iOS.
If you already have test suites, the docs cover running Appium sessions, Maestro flows and an @e2e-dev/eas integration that starts one session per e2e worker. You may not need to rewrite anything.
Cost, limits and things to watch
Pricing is usage-based: $0.10 per simulator minute on iOS and Android. Starter, Production and Enterprise plans each include 60 minutes a month until 2027. The free allowance goes quickly. Seven parallel agents running 30-minute sessions use 210 minutes, which is $15 after the included hour, on every such run. That's cheap compared with buying Mac hardware, but it adds up if you leave verification running on every agent iteration.
There are a few caveats the announcement doesn't stress:
- Sessions outlive your process. Closing the terminal, returning from --non-interactive or ending a CI job doesn't stop the device. The blog says no session runs longer than two hours. Always set --max-duration-minutes and an idle timeout.
- Parallelism has limits. Unite.AI, citing the billing guide, reports that sessions use your account's job concurrency and wait in a queue when none is free. Parallel EAS Builds and simulator sessions compete for the same capacity.
- One session per directory. .env.eas-simulator holds a single session. To run parallel agents, use a separate checkout or worktree for each one, or use --out-config-type env and pass --id explicitly.
- Secrets on disk. That file contains controller tokens. Add it to .gitignore, and don't upload it as a CI artifact or print it.
- Uploads are capped. Builds uploaded through the dashboard can be at most 1 GB, as an APK or an iOS Simulator .tar.gz.
Teams already on a paid EAS plan with agents writing UI code should try this. The recorded evidence alone makes agent-written PRs easier to trust. Solo developers on a Mac whose agent runs locally get less out of it: your local simulator is free, and the main gains are session history and freed-up RAM. Nothing in your app code changes either way, so you can wait without risk.






