Run Android checks in GitHub Actions without an emulator.
Why not the emulator in CI
The Android emulator needs hardware acceleration and gigabytes of SDK and system images. On hosted CI runners, booting it is often the slowest and flakiest step of the job. A cloud phone is already an Android device: the runner only needs Node.js and network access.
| Emulator on the runner | Phonebox phone | |
|---|---|---|
| Setup in the job | SDK, system image, KVM, boot and wait | npm install the CLI |
| Between runs | Starts fresh every time | Parked: keeps the app's data and test accounts |
| Cost | Runner minutes while it boots and runs | $0.06/min while the phone runs, parked free |
| Watch a failure | Logs and screenshots | A live view link or a screen recording |
The workflow
Create one phone for CI, then save its ID as the repository variable PHONEBOX_PHONE and an API key as the secret PHONEBOX_API_KEY. Then add a workflow:
name: Android smoke check
on: pull_request
concurrency: phonebox-phone # one run per phone at a time
jobs:
smoke:
runs-on: ubuntu-latest
env:
PHONEBOX_API_KEY: ${{ secrets.PHONEBOX_API_KEY }}
PHONEBOX_PHONE: ${{ vars.PHONEBOX_PHONE }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: 17 }
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: ./gradlew assembleDebug
- run: npm install -g https://phonebox.dev/downloads/phonebox-0.1.0.tgz
- run: phonebox start
- run: phonebox apps install app/build/outputs/apk/debug/app-debug.apk
- run: phonebox open com.example.app
- run: phonebox wait --text "Welcome" --timeout 30s
- if: always()
run: phonebox parkconcurrency keeps two runs from driving the same phone at once. phonebox wait exits with code 3 when the text doesn't appear in time, which fails the step. The last step parks the phone even when a check fails. For a wider exploratory pass, run a coding agent in the job with the MCP server and ask it to walk the screens the pull request changed.
More ways to use a cloud phone
Questions
Can I run Android UI tests in GitHub Actions without an emulator?
Yes. Install the Phonebox CLI in the job, start a cloud Android phone with phonebox start, install the APK you just built with phonebox apps install, check the screen with phonebox wait or phonebox look, and park the phone at the end. The runner needs Node.js 20 and network access, not KVM or an Android system image.
Do I need a new phone for every CI run?
No. Reuse one phone: a parked phone keeps the installed app, its data and any signed-in test accounts, and costs nothing between runs. Use a concurrency group so two runs never drive the same phone at once.
What does it cost per CI run?
$0.06 per phone-minute while the phone runs, billed per second with a one-minute minimum. Parked phones are free and keep their apps and sign-ins. New accounts get $2 of credit with no card.