CLI Reference

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 a bin/ 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 back
    • zipconduit — 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.


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 report
  • report.json -- machine-readable JSON report
  • junit-report.xml -- JUnit XML for CI systems (GitHub Actions, Jenkins, GitLab CI)
  • allure-results/ -- Allure-compatible results directory
  • maestro-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 flows
  • env -- environment variables (CLI -e flags 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.