Quick answer
A CircleCI job can start an Expo Simulators session, install your app, run checks, and stop the session. The job signs in with an EXPO_TOKEN from a robot user, starts the session with eas simulator:start --non-interactive, and drives the app with agent-device through eas simulator:exec. The iOS Simulator runs on a Mac on EAS, so a Linux Docker executor is enough.
With Expo Simulators, your CircleCI pipeline can open your iOS app on a simulator, tap through a flow, and save a screenshot and a recording.
What you need
- Expo Simulators access for the account that owns the project.
- An app config that contains the EAS project ID.
- A build profile for the iOS Simulator. These builds are not signed, so EAS Build does not ask for credentials.
Store the Expo token in CircleCI
CI jobs cannot use eas login. EAS CLI reads EXPO_TOKEN from the environment.
- On expo.dev, create a robot user on the account that owns the project. Give it the Developer role or higher.
- Create an access token for the robot user.
- In CircleCI, open Org > Contexts, create a context named
expo-simulators, and addEXPO_TOKENto it. A project environment variable also works for one project.
Add the job
What each step does
- Image.
cimg/nodeis a CircleCI image with Node.js andjq. agent-device needs Node.js 22.12 or later. - Access check.
simulator:availabilityexits with code 0 even without access.jq -efails the step whenavailableisfalse. - Build. The step waits for EAS Build and saves the build details to
build.json. CircleCI stops a step that prints nothing for 10 minutes, so two steps setno_output_timeout. - Start.
--non-interactivereturns when the simulator is ready and writes the connection details to.env.eas-simulator. Eachrunstep is a new shell in the same container, so later steps read the file. Keep the default file output. - Drive.
simulator:execloads the file and runs agent-device against the session. agent-device saves screenshots and recordings to the paths you pass. Use yourios.bundleIdentifierfromnpx expo config --jsonand a label from your app.
To skip the build, pass --build-fingerprint <hash> in place of --build-id. EAS installs the newest finished build with that fingerprint. Its native code matches, and its JavaScript can be older than your branch.
For Android, use --platform android and an APK build. Android support is still in development, so some features that work on iOS may not work on Android yet.
Always stop the session
A CI job that ends does not stop the session. CircleCI runs a teardown step when the job ends, also after a failure, a cancel, or a timeout. A last step with when: always runs after a failed step, and CircleCI skips it on a cancel or a timeout. Running simulator:stop on a stopped session succeeds.
--max-duration-minutes is the backup. If the stop step does not run, the session stops at that time.
Keep tokens out of the logs
.env.eas-simulator holds the controller tokens for the session. CircleCI masks only the values that you store in contexts and project settings, so it does not mask these tokens.
- Do not print the file, and do not upload it. The config uploads only
artifacts. - Do not add
--jsontosimulator:startin CI. Its output includes the connection details. - Do not use
set -xin these steps. CircleCI warns that it can print secrets.
Let an agent set it up
If an agent writes the config or runs the session, have it load these Expo Skills first:
expo-overview: the entry point that routes the agent to the right Expo skill.eas-simulator: the currenteas simulatorcommands, the access check, and the cleanup steps.
Install them:
- Claude Code:
claude plugin install expo@claude-plugins-official - Codex:
codex plugin add expo@openai-curated - Cursor and other agents:
npx skills add expo/skills
The full list is at docs.expo.dev/skills.
Try it
Expo Simulators is in a limited beta release. The CI docs also cover the REST API, parallel sessions, and e2e tests.
Verified on 20 October 2026.