Quick Start
# Run a single flow
maestro-runner flow.yaml
# Run top-level flows in a directory (does NOT recurse into subdirectories)
maestro-runner flows/
# Run multiple files
maestro-runner login.yaml checkout.yaml settings.yaml
The test subcommand is optional -- maestro-runner flow.yaml and maestro-runner test flow.yaml are identical.
Directory Scanning Behavior
When a directory is passed, only top-level .yaml/.yml files are collected. Subdirectories are ignored by default. Files named config.yaml or config.yml are always excluded (treated as workspace config).
To include subdirectory flows, create a config.yaml in the directory:
# config.yaml - include flows from subdirectories
flows:
- "auth/*" # Top-level files in auth/
- "checkout/*" # Top-level files in checkout/
- "**" # All .yaml/.yml files recursively
- "**/smoke*.yaml" # Recursive, matching a filename pattern
# With config.yaml in flows/ directory, subdirectory flows are included
maestro-runner flows/
# Or reference individual files/subdirectories directly
maestro-runner flows/auth/login.yaml flows/checkout/cart.yaml
Commands
test (default)
Run Maestro flows on a device. This is the default command -- you can omit it.
maestro-runner [global-flags] [test] [test-flags] <flow-file-or-folder>...
At least one flow file or folder path is required.
wda version
Show the installed WebDriverAgent version.
maestro-runner wda version
wda update
Update WebDriverAgent to the latest version (or a specific version).
# Update to latest
maestro-runner wda update
# Update to specific version
maestro-runner wda update 8.8.3
# Check only (don't install)
maestro-runner wda update --check
| Flag | Type | Description |
|---|---|---|
--check |
bool | Only check if an update is available |
Global Flags
Available on all commands. These can appear before or after the test keyword.
| Flag | Alias | Type | Default | Env Var | Description |
|---|---|---|---|---|---|
--platform |
-p |
string | (auto-detect) | MAESTRO_PLATFORM |
Platform: ios, android, web |
--device |
--udid |
string | (auto-detect) | MAESTRO_DEVICE |
Device ID (comma-separated for multiple) |
--driver |
-d |
string | uiautomator2 |
MAESTRO_DRIVER |
Driver: uiautomator2 (Android default), devicelab (Android), wda (iOS, auto-selected), appium (both platforms) |
--appium-url |
string | http://127.0.0.1:4723 |
APPIUM_URL |
Appium server URL (for appium driver) | |
--caps |
string | APPIUM_CAPS |
Path to Appium capabilities JSON file | ||
--verbose |
bool | false |
MAESTRO_VERBOSE |
Enable verbose logging | |
--app-file |
string | MAESTRO_APP_FILE |
App binary (.apk/.app/.ipa) to install before testing | ||
--no-app-install |
bool | false |
MAESTRO_NO_APP_INSTALL |
Skip app installation even if --app-file is provided |
|
--no-driver-install |
bool | false |
MAESTRO_NO_DRIVER_INSTALL |
Skip driver installation (UIAutomator2, WDA, DeviceLab). Useful when drivers are already installed on the device | |
--no-ansi |
bool | false |
Disable ANSI colors | ||
--team-id |
string | MAESTRO_TEAM_ID, DEVELOPMENT_TEAM |
Apple Development Team ID for WDA code signing. Optional when a booted simulator is auto-detected | ||
--start-emulator |
string | MAESTRO_START_EMULATOR |
Start Android emulator by AVD name (e.g., Pixel_7_API_33) |
||
--start-simulator |
string | MAESTRO_START_SIMULATOR |
Start iOS simulator by name or UDID (e.g., iPhone 15 Pro) |
||
--auto-start-emulator |
bool | false |
MAESTRO_AUTO_START_EMULATOR |
Auto-start an emulator/simulator if no devices found | |
--shutdown-after |
bool | true |
MAESTRO_SHUTDOWN_AFTER |
Shutdown devices started by maestro-runner after tests | |
--boot-timeout |
int | 180 |
Device boot timeout in seconds |
Test Flags
These flags apply only to the test command (or the default action).
| Flag | Alias | Type | Default | Env Var | Description |
|---|---|---|---|---|---|
--config |
string | Path to workspace config.yaml |
|||
--env |
-e |
string (repeatable) | Environment variables (KEY=VALUE), can be specified multiple times |
||
--include-tags |
string (repeatable) | Only include flows with these tags | |||
--exclude-tags |
string (repeatable) | Exclude flows with these tags | |||
--output |
string | ./reports |
Output directory for reports | ||
--flatten |
bool | false |
Don't create timestamp subfolder (requires --output) |
||
--parallel |
int | Run tests in parallel on N devices | |||
--wait-for-idle-timeout |
int | 200 |
MAESTRO_WAIT_FOR_IDLE_TIMEOUT |
Wait for device idle in ms (0 = disabled) | |
--typing-frequency |
int | 30 |
MAESTRO_TYPING_FREQUENCY |
WDA typing speed in keys/sec. Lower values help React Native apps where the JS bridge can't keep up at full speed | |
--no-flutter-fallback |
bool | false |
Disable automatic Flutter VM Service fallback for element finding | ||
--headed |
bool | false |
Show browser window (web platform only) | ||
--browser |
string | chromium |
MAESTRO_BROWSER |
Browser: chrome, chromium, or path to binary (web platform only) |
Environment Variables
All flags with an Env Var column can be set via environment variable instead of the CLI flag. CLI flags take precedence over environment variables.
Additionally:
-
NO_COLOR-- disables ANSI color output (standard convention) -
MAESTRO_RUNNER_HOME-- overrides the home directory for drivers and cache. Resolution order: (1) this env var, (2) parent of binary's directory if binary is in abin/subdirectory, (3) current working directory -
MAESTRO_RUNNER_IOS_INSTALLER-- controls which installer is used for iOS apps on physical devices. Values:- (unset, default) — prefer
xcrun devicectl device install app, fall back to the go-ios zipconduit path if devicectl isn't available devicectl— force devicectl, do not fall backzipconduit— force the legacy go-ios zipconduit path (useful on older macOS / Xcode that don't ship devicectl)
Both paths run under a 3-minute timeout so a stuck install surfaces as an error instead of an infinite spinner. The simulator install path (
xcrun simctl install) is also timeout-protected and is not controlled by this variable. - (unset, default) — prefer
Tag Filtering
Flows define tags in their YAML config:
tags:
- smoke
- login
Filter which flows run:
# Only run flows tagged "smoke"
maestro-runner --include-tags smoke flows/
# Run all flows except those tagged "flaky"
maestro-runner --exclude-tags flaky flows/
# Multiple tags (repeat the flag)
maestro-runner --include-tags smoke --include-tags regression flows/
# Combine include and exclude
maestro-runner --include-tags smoke --exclude-tags slow flows/
Logic:
--include-tags: Flow must have at least one matching tag to be included.--exclude-tags: Flow is excluded if it has any matching tag.- If both are specified, exclude takes precedence.
- If neither is specified, all flows run.
Output & Reports
Reports are generated automatically after every run.
# Default: ./reports/<timestamp>/
maestro-runner flows/
# Custom directory: ./my-reports/<timestamp>/
maestro-runner flows/ --output ./my-reports
# Flat (no timestamp subfolder): ./my-reports/
maestro-runner flows/ --output ./my-reports --flatten
Generated files in the output directory:
report.html-- visual HTML reportreport.json-- machine-readable JSON reportjunit-report.xml-- JUnit XML for CI systems (GitHub Actions, Jenkins, GitLab CI)allure-results/-- Allure-compatible results directorymaestro-runner.log-- detailed execution log
Driver Selection
UIAutomator2 (default, Android)
Direct communication with the UIAutomator2 server on the device. No external dependencies.
maestro-runner flows/
maestro-runner --driver uiautomator2 flows/
DeviceLab (Android, optional)
An alternative Android driver that runs automation directly on the device via WebSocket, bypassing the UIAutomator2 HTTP layer. ~2x faster than UIAutomator2 and ~5x faster than Maestro CLI. All existing Maestro YAML flows work as-is — no changes needed.
maestro-runner --driver devicelab flows/
maestro-runner --driver devicelab --device emulator-5554 flows/
The DeviceLab driver also includes bounds stabilization for animated elements (waits for element position to settle before clicking), improved special character handling in text selectors, and WebView CDP and Chrome browser CDP support on Android.
| UIAutomator2 | DeviceLab | |
|---|---|---|
| Speed | Baseline | ~2x faster |
| Transport | HTTP (adb forward) | WebSocket (on-device) |
| WebView CDP | No | Yes (Android) |
| Chrome CDP | No | Yes (Android) |
| Setup | None (default) | --driver devicelab |
| Compatibility | All flows | All flows |
WDA (iOS, auto-selected)
When --platform ios is set and the default driver is used, maestro-runner automatically selects the WDA (WebDriverAgent) driver.
maestro-runner --platform ios flows/
Typing Speed (iOS)
The --typing-frequency flag controls how fast WDA types characters on iOS. Default is 30 keys/sec (WDA's built-in default is 60).
# Slow down typing for React Native apps
maestro-runner --platform ios --typing-frequency 15 flows/
Or set per-flow in the YAML config section:
appId: com.example.app
typingFrequency: 20
---
- inputText: "hello world"
Priority: Flow YAML typingFrequency > CLI --typing-frequency > Default (30)
Appium (both platforms, cloud providers)
Connects to an external Appium server. Required for cloud providers (BrowserStack, SauceLabs, LambdaTest).
# Local Appium server
maestro-runner --driver appium flows/
# With capabilities file
maestro-runner --driver appium --caps caps.json flows/
# Cloud provider
maestro-runner --driver appium \
--appium-url "https://your-cloud-provider/wd/hub" \
--caps cloud-caps.json \
flows/
Capabilities file (caps.json): Standard Appium JSON capabilities. CLI flags (--platform, --device, --app-file) override values in the caps file.
App Installation
Install an app before testing with --app-file. This ensures you always test the right build.
# Android
maestro-runner --app-file app.apk flows/
# iOS simulator
maestro-runner --platform ios --app-file App.app flows/
# iOS real device
maestro-runner --platform ios --team-id ABCDE12345 --app-file App.ipa flows/
If --app-file is not set, maestro-runner runs against whatever is already installed on the device.
Emulator & Simulator Management
maestro-runner can start and manage Android emulators and iOS simulators.
Android Emulator
# Start a specific AVD before running tests
maestro-runner --start-emulator Pixel_7_API_33 flows/
# Auto-start if no devices found
maestro-runner --auto-start-emulator flows/
# Keep emulator running after tests
maestro-runner --start-emulator Pixel_7_API_33 --shutdown-after=false flows/
iOS Real Device
# Run on a connected real device (team ID required for code signing)
maestro-runner --platform ios --team-id ABCDE12345 flows/
# Specify a device by UDID
maestro-runner --platform ios --team-id ABCDE12345 --device 00001234-ABCDEF012345 flows/
iOS Simulator
# Start a named simulator
maestro-runner --platform ios --start-simulator "iPhone 15 Pro" flows/
# Auto-start if no simulators are booted
maestro-runner --platform ios --auto-start-emulator flows/
Parallel Execution with Auto-Start
# Start 3 emulators and run tests in parallel
maestro-runner --platform android --parallel 3 --auto-start-emulator flows/
# Start 2 iOS simulators in parallel
maestro-runner --platform ios --parallel 2 --auto-start-emulator flows/
Each parallel emulator requires a unique AVD (Android locks the AVD directory at boot, preventing the same AVD from running twice). For iOS, simulators are auto-created if not enough shutdown simulators exist.
Devices started by maestro-runner are automatically shut down after tests unless --shutdown-after=false is set. Cleanup also runs on SIGINT (Ctrl+C) and SIGTERM.
Parallel Execution
Run tests across multiple devices simultaneously.
# Auto-detect devices
maestro-runner --parallel 2 flows/
# Specific devices
maestro-runner --device emulator-5554,emulator-5556 flows/
# Auto-start emulators to reach target count
maestro-runner --parallel 3 --auto-start-emulator flows/
During parallel execution, brief status updates are shown to avoid interleaved output. Detailed results are displayed after all tests complete.
iOS simulator auto-creation: When using --parallel with --auto-start-emulator on iOS, if not enough shutdown simulators exist, maestro-runner automatically creates new simulators matching the latest available runtime. Created simulators are deleted on shutdown.
In-use detection: Devices already in use by another maestro-runner instance are automatically skipped during device selection. Detection works via WDA port checks (iOS) and Unix socket checks (Android).
Parallel with the Appium driver
--parallel N also works with --driver appium. Instead of allocating N local devices, it opens N Appium sessions against the same --appium-url — the Appium server (cloud provider or local hub) assigns each session to a device.
# 3 parallel sessions against Sauce Labs
maestro-runner --driver appium --parallel 3 \
--appium-url "https://USER:[email protected]/wd/hub" \
--caps caps.json flows/
The parallel count is capped at the number of flows — if you pass --parallel 10 with only 4 flows, maestro-runner warns and uses 4 workers. Each flow runs to completion on one session; sessions are reused across flows until the flow queue drains.
Workspace Config
A config.yaml file can set shared configuration for all flows:
maestro-runner --config config.yaml flows/
The config file can define:
appId-- default app ID for all flowsenv-- environment variables (CLI-eflags take precedence)waitForIdleTimeout-- idle timeout override
Version Information
maestro-runner --version
Exit Codes
| Code | Meaning |
|---|---|
0 |
All flows passed |
1 |
One or more flows failed, or a runtime error occurred |
Key Differences from Maestro CLI
| Feature | Maestro CLI | maestro-runner |
|---|---|---|
| Binary name | maestro |
maestro-runner |
| Test command | maestro test (required) |
maestro-runner flows/ or maestro-runner test flows/ |
| Default driver | Maestro's own driver | uiautomator2 (direct) or wda (iOS) |
| Appium support | No | --driver appium with --caps |
| Cloud providers | No | Via --driver appium --appium-url |
| Device start (Android) | maestro start-device |
--start-emulator <avd> |
| Device start (iOS) | maestro start-device |
--start-simulator <name> |
| Auto device start | No | --auto-start-emulator |
| Parallel execution | No | --parallel N |
| WDA management | Built-in | maestro-runner wda update |
| Reports | HTML/JUnit | HTML + JSON + log |
| Environment vars | maestro test -e KEY=VAL |
Same: -e KEY=VAL |
| Tag filtering | --include-tags, --exclude-tags |
Same |
Troubleshooting
device already in use
Error: device XXXXX is already in use
Another maestro-runner instance may be using this device.
Socket: /tmp/uia2-XXXXX.sock
This means another maestro-runner process is actively running tests on this device. Wait for it to finish, or use --device to target a different device.
If you believe this is a stale error (e.g., after a crash), it should auto-resolve on the next run. If it persists, manually remove the files:
rm /tmp/uia2-<serial>.sock /tmp/uia2-<serial>.pid
Keyboard covering target element
If taps fail after inputText or inputRandom with an error like "keyboard covering element", add - hideKeyboard after the input step:
- inputText: "[email protected]"
- hideKeyboard
- tapOn: "Submit"
maestro-runner automatically detects when the soft keyboard covers a target element and suggests this fix in the error message.
clearState fails on iOS real device (Appium)
If clearState fails with mobile: clearApp unsupported, use newSession: true on launchApp instead:
- launchApp:
appId: com.example.app
newSession: true
This creates a fresh Appium session, providing clean state without relying on mobile: clearApp.