Skip to main content

Hosted Device Farms

Tier 4 — runs mobile tests on real devices in BrowserStack App Automate or Sauce Labs Real Device Cloud. Best for iOS coverage without owning Macs, real-device behavior (sensors, biometrics, OEM-specific bugs), and burst capacity.

The platform handles vendor-specific quirks (auth, APK upload, capability blocks) automatically. Same YAML, same Job History.

What the platform does for you

  1. Auto-upload the APK — agent fetches your stored APK from MinIO and uploads it to the vendor at job time. No manual curl to /app-automate/upload.
  2. Credentials from the vault — vendor keys live once in the environment credential vault, referenced by tag.
  3. Vendor-specific capability blocksbstack:options for BrowserStack, sauce:options for Sauce Labs are built automatically with the right structure.
  4. Region-aware URLs — Sauce Labs hub URL is templated by region (us-west-1, eu-central-1, apac-southeast-1).

Quick start: BrowserStack

1. Store credentials in the vault

In your environment's auth config, add a credential set:

{
"credentials": [{
"tag": "prod-browserstack",
"username": "your-bs-username",
"password": "your-bs-access-key",
"isDefault": false
}]
}

The password field carries the access key (semantically the secret). Encrypted at rest with AES-GCM.

You can have multiple credential sets — e.g. dev-browserstack and prod-browserstack. Set one as the default with defaultCredentialTag on the environment if you want apps to use it implicitly.

2. Configure the mobile app

In Configuration → Mobile Apps, edit your app:

FieldValue
Cloud providerBROWSERSTACK
Cloud config (JSON)See below
{
"credentialTag": "prod-browserstack",
"deviceName": "Google Pixel 7",
"osVersion": "13.0",
"projectName": "MyApp Tests"
}

That's it. No appUrl, no userName, no accessKey in the cloud config — the platform resolves the tag from the vault at job time.

3. Make sure your APK is uploaded

Through Mobile Apps → Upload Binary (the existing flow). The platform stores it in MinIO.

4. Run a test

Any existing mobile test against this app. The agent will:

  1. Download the APK from MinIO
  2. Upload it to https://api-cloud.browserstack.com/app-automate/upload
  3. Connect to https://hub-cloud.browserstack.com/wd/hub with bs://<uploaded-hash> as the app capability
  4. Run the test on the requested device

Watch the agent log:

DF.1.c — using resolved cloud_config from vault (provider=BROWSERSTACK)
DF.1.b — no appUrl in cloud_config; auto-uploading APK to BrowserStack
DF.1.b — BrowserStack upload OK, app_url=bs:**...***
Creating BrowserStack driver: hub=https://hub-cloud.browserstack.com/wd/hub, device=Google Pixel 7

The test then runs on the device and reports back to Job History as usual.

5. (Optional) Pre-uploaded apps for faster startup

Per-job upload adds ~5 seconds. For high-frequency CI, upload once via the BrowserStack REST API and put the returned bs://... URL in cloud_config.appUrl:

{
"credentialTag": "prod-browserstack",
"appUrl": "bs://abc123def456",
"deviceName": "Google Pixel 7"
}

When appUrl is set, the agent skips the auto-upload step. Update the URL when the APK version changes.

Quick start: Sauce Labs

Same shape as BrowserStack. Differences:

AspectBrowserStackSauce Labs
Credential mappingusername + accessKeyusername + accessKey (same)
Upload endpoint/app-automate/upload/v1/storage/upload
App reference shapebs://abc123...storage:filename=app.apk
Hub URLhub-cloud.browserstack.comondemand.{region}.saucelabs.com
Options blockbstack:optionssauce:options
Regionsone globalus-west-1 (default), eu-central-1, apac-southeast-1

Cloud config example

{
"credentialTag": "prod-saucelabs",
"region": "eu-central-1",
"deviceName": "Google Pixel 7",
"platformVersion": "13.0"
}

That's all. The agent will:

  • Upload APK to https://api.eu-central-1.saucelabs.com/v1/storage/upload
  • Connect to https://ondemand.eu-central-1.saucelabs.com/wd/hub
  • Run the test on the requested device

Credential pattern: default vs choose

The platform follows the same vault precedence pattern as API scenarios:

What you setWhat happens
cloud_config.credentialTag: "prod-bs"Explicit — use that vault entry
No credentialTag, env has a defaultCredentialTagDefault — use the env's default credential
No credentialTag, env has a credential with isDefault: trueDefault — use that one
Neither tag nor default existAgent passes through; provider validation surfaces "missing userName"
Inline userName + accessKey set (legacy)Pass through unchanged

Most teams set one credential per environment + flag it as default. Then mobile apps just need { "deviceName": "..." } — no credential boilerplate.

Switching an existing test

Already running on T1 (local) and want to graduate to a real device? Only change cloud_provider:

Cloud provider: NONE → BROWSERSTACK
Cloud config: {} → {
+ "credentialTag": "prod-browserstack",
+ "deviceName": "Google Pixel 7"
+ }

The YAML doesn't change. The agent automatically:

  • Downloads your APK from MinIO (already there from T1 setup)
  • Uploads it to BrowserStack
  • Connects to the right hub
  • Runs the test
  • Reports results to the same Job History

Device matrix (multiple devices)

Today: configure one device per mobile app. To run against a matrix:

  • Clone the mobile app once per device (or per OS version)
  • Each clone has a different deviceName / osVersion in cloud_config
  • Run the same test against each — they appear as separate runs in Job History

Future: declarative matrix on a single test (devices: ["Pixel 7", "Galaxy S23", "iPhone 14"]) — on the roadmap.

Cost sanity check

For ~1000 mobile-test runs / month, 5 min average:

ServiceApprox monthly cost
BrowserStack ($199/parallel/mo)~$200 — fine for low volume
Sauce Labs (similar tier)~$199
T2 self-hosted on AWS c5.metal~$500 + ops

Above ~3 hours/day of test execution, T2 self-hosted becomes more cost-effective. Below that, vendors win on cold-start and zero ops.

What's NOT supported yet

  • iOS auto-upload — both vendors accept .ipa via the same endpoints. The agent currently only runs Android tests, so iOS hasn't been exercised end-to-end.
  • AWS Device Farm — different execution model (async, not real-time WebDriver). On the roadmap.
  • URL caching — every job re-uploads the APK. Future optimization caches the bs://... URL on the mobile app after first upload.

Troubleshooting

"BrowserStack 401 Unauthorized"

Vault credential is wrong. Check the tag, username, and password against the BrowserStack dashboard.

"credentialTag 'X' not found in environment N's credential vault"

The error message lists available tags. Either fix the tag name in cloud_config or add the missing entry to the env vault.

"No appUrl in cloud_config and no app_path on mobile app"

Either upload an APK first via Upload Binary, or set cloud_config.appUrl to a pre-uploaded vendor URL.

Test runs but device behaves oddly

Check the bstack:options debug capability is on (it's default-true in our config). View the session video in the BrowserStack dashboard.

Next steps