Library / SDK
Git-Agni/prod-FARM-IOS-Core avatar
Git-Agni/prod-FARM-IOS-Core

Phone Farm iOS: running a real iPhone farm from a Mac

A farm of real iPhones, run from your Mac. Open-source iOS device automation with live control, a Postgres-backed scheduler, and TikTok workflows. Self-hosted, Apache-2.0.

1,474 stars279 forksTypeScriptApache-2.0

At a glance

What is it?
Git-Agni/prod-FARM-IOS-Core is a self-hosted TypeScript application that drives physical iPhones through WebDriverAgent and Appium, schedules TikTok workflows in PostgreSQL, and exposes a local dashboard. Here is what it actually requires, and where it stops being the right tool.
Who is it for?
Adopt it if you already own the iPhones, already have an Apple signing identity, and want scheduled TikTok or app-driving work to run on a Mac you control. Do not adopt it if you need Android, if you want a hosted service, or if nobody on the team can produce a signed WebDriverAgent build.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 16, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem: real iPhones, not emulators

Simulators are cheap and reproducible, and they are also not what a TikTok upload endpoint sees. Phone Farm iOS exists for the case where the target application behaves differently on real hardware: camera, attestation, device identifiers, or a mobile app that simply refuses to run in a simulator. The README frames the project as "a farm of real iPhones, run from your Mac," and the repository topics list appium, webdriveragent, device-farm and tiktok-automation-script. That combination tells you the intended user: someone with a shelf of physical devices, a Mac to drive them, and a workflow repetitive enough to schedule.

It is not a cloud device service. There is no hosted control plane in the repository, no account tier, and no managed device pool. The whole thing runs on your machine, against your hardware, with your Apple signing identity. That is the appeal and the constraint in one sentence.

Four long-lived processes and a Postgres queue

The architecture, as the README describes it, is four processes plus data stores. Appium listens on 127.0.0.1:4725 with the XCUITest driver, WebDriverAgent runs as a service on the devices, a worker pulls scheduled jobs, and a web server serves the dashboard and API on port 3000. The README explicitly says to wrap each one in a launchd agent or systemd unit for an always-on host, which is an honest admission that nothing here supervises itself.

Scheduling state lives in PostgreSQL, started through docker-compose.yml as postgres:17-alpine bound to 127.0.0.1. Tasks are persisted with pluginId, taskType, taskVersion and a JSON payload. That four-field shape is the most interesting design decision in the repository: because the version travels with the stored task, an old schedule cannot silently pick up a new contract when a plugin is upgraded. Anyone who has watched a cron job quietly change behaviour after a deploy will recognise why that matters.

Plugins are the extension point. src/plugin.ts defines the stable interfaces, and a plugin can contribute versioned tasks, registration checks, device-page panels, namespaced HTTP routes and declared WDA extensions. The README states that production plugins should be separate packages and should never require changes to core routing or scheduler code. src/example-plugin.ts is the minimal open-app example.

Installing it and registering a first device

The README lists the requirements plainly: Node 22 or newer, PostgreSQL, Xcode, a signed real-device WebDriverAgent, and Appium's XCUITest driver. Start from a clone of the repository and install dependencies, then copy the environment template.

bash
npm install
cp .env.example .env
npm run appium:install-driver

The driver install pins xcuitest@7.26.3 under APPIUM_HOME=.appium2. Next, bring up the database and run migrations. Note that docker-compose.yml uses ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}, so the compose file will refuse to start until you replace CHANGE_ME in .env.

bash
npm run db:up
npm run db:migrate

WebDriverAgent has to be built and signed for your own Apple identity. The .env.example file carries XCODE_ORG_ID, XCODE_SIGNING_ID (defaulting to Apple Development) and WDA_BUNDLE_ID, and the README calls the signing material required for WDA build/signing on a physical device.

bash
npm run wda:prepare

With that done, the four processes run in separate terminals or under a supervisor. The web server binds to WEB_HOST=127.0.0.1 and WEB_PORT=3000 by default.

bash
npm run appium
npm run wda:service
npm run worker
npm run web

Device-specific values live in devices.json, pointed at by DEVICES_CONFIG_PATH. The .env.example is explicit that unlock passcodes go in that file under a "passcode" key per entry, that it is git-ignored and written 0600, and that they do not belong in .env. TikTok support is on by default, with TIKTOK_BUNDLE_ID set to com.zhiliaoapp.musically. Additional task plugins are added through PHONE_FARM_PLUGINS as a comma-separated list of ESM package names, and the file notes that an empty value is a valid fleet-only installation.

The authentication boundary is a startup failure, on purpose

Most self-hosted tools make exposure a documentation problem: a warning in the README, a default password, and a user who binds to 0.0.0.0 anyway. This project makes it a runtime problem. The README states that startup deliberately fails if WEB_HOST is moved away from loopback without PHONE_FARM_AUTH_PLUGIN set to an ESM authentication provider. The .env.example repeats the constraint in a comment: the variable is "Required before WEB_HOST is changed away from loopback."

That is a good default and a real limitation at the same time. If you want the dashboard reachable from another machine, you cannot simply flip a host variable; you have to write and publish an AuthProvider package first. For a single operator on one Mac, the loopback default is fine and the whole authentication question never comes up. For a team, the missing piece is the provider, and the repository does not ship one beyond the interface.

What it will not do for you

Three constraints stand out. First, the platform is iOS only, on macOS, with Xcode in the loop. There is no Android path in the repository and no Linux server story, because WebDriverAgent signing requires Apple tooling. A phone farm built on Android would use a different stack entirely.

Second, the signing identity is yours to manage. XCODE_ORG_ID and XCODE_SIGNING_ID must be filled in, and WDA must be rebuilt when certificates expire or devices are added. The repository ships Patches/, which suggests the WebDriverAgent build is not stock, but the README does not document what those patches change or how they survive a WDA upgrade. Treat that as an open question rather than a solved one.

Third, process supervision is out of scope. The README tells you to wrap each of the four processes in launchd or systemd, and nothing in the repository suggests a bundled supervisor, health check or restart policy. If the worker dies at 3am, the schedule stops until someone notices. That is the cost of a local-first design, and it is worth stating plainly rather than treating as a footnote.

How this differs from Appium alone

Appium is the obvious alternative, and it is not a competitor so much as a component: this project installs Appium's XCUITest driver and runs an Appium server on port 4725. The difference is everything layered above it. Plain Appium gives you a session-oriented automation API and leaves device inventory, job persistence, retries, execution history, uploads and a UI to you. Phone Farm iOS adds a Postgres-backed scheduler, a worker that executes persisted tasks, a dashboard, guided device registration, and a plugin contract with versioned task definitions.

If your workload is a test suite that runs in CI against a couple of devices, Appium plus your existing test runner is the smaller answer, and adding a scheduler and a database would be overhead. If your workload is recurring, stateful, and spread across a rack of devices that must be individually registered and monitored, the scheduler and the task versioning are the parts you would otherwise write yourself. The honest dividing line is whether you need execution history and recurring jobs at all.

Licence, maintenance and the upgrade question

The project is Apache-2.0, and the repository carries both a LICENSE and a NOTICE file, which is the normal Apache-2.0 arrangement: you can use it commercially, modify it, and redistribute it, provided you keep the notices and state significant changes. The package.json declares version 0.1.0-review.0, which reads as pre-1.0 and therefore as an API that can still move. No releases were retrieved, so there is no published changelog to consult before upgrading.

The last push to the default branch was on 2026-09-07, nine days before this writing, so the repository is not dormant. It is also not archived. What the repository does not contain is a versioning policy for the plugin interface, a migration guide between core versions, or a statement about backward compatibility for PHONE_FARM_PLUGINS packages. The taskVersion field protects stored schedules from contract drift, which is a partial answer, but it does not tell an operator how much work a core upgrade will cost. Budget for reading the diff yourself.

Editorial conclusion

Adopt it if you already own the iPhones, already have an Apple signing identity, and want scheduled TikTok or app-driving work to run on a Mac you control. Do not adopt it if you need Android, if you want a hosted service, or if nobody on the team can produce a signed WebDriverAgent build. Verify three things before committing: that npm run wda:prepare succeeds against your XCODE_ORG_ID and XCODE_SIGNING_ID, that the Postgres container in docker-compose.yml comes up with a non-default POSTGRES_PASSWORD, and that PHONE_FARM_AUTH_PLUGIN is set before WEB_HOST ever leaves 127.0.0.1, because the server refuses to start in that case.

Frequently asked questions

Is Apple iOS open source?

No. The project's own README describes it as an open-source application that operates physical iOS devices; iOS itself is Apple's proprietary operating system, and the repository depends on Apple tooling such as Xcode and a signed WebDriverAgent build.

What do I need installed before running Phone Farm iOS?

The README lists Node 22 or newer, PostgreSQL, Xcode, a signed real-device WebDriverAgent, and Appium's XCUITest driver. The database is normally started through the bundled docker-compose.yml service.

Can Phone Farm iOS run Android devices?

Nothing in the repository describes an Android path. The requirements are macOS-centric: Xcode, a signed real-device WebDriverAgent, and Appium's XCUITest driver.

Where are device unlock passcodes stored in Phone Farm iOS?

In devices.json, the file referenced by DEVICES_CONFIG_PATH, under a "passcode" key per device entry. The .env.example states that this file is git-ignored and written 0600, and that passcodes do not belong in .env.

Official sources

  1. Git-Agni/prod-FARM-IOS-Core on GitHub
  2. Issues
  3. License: Apache-2.0
  4. Project website
  5. README
Community notes

Community notes