Search

Guides

Multi-Instance Android Emulator Farming with Device Groups

Multi-instance Android emulator farming in MAS: add each BlueStacks or LDPlayer instance by port, group them, and give every device its own profile and proxy.

  • Windows
  • Mac
  • Emulator
  • Studio
  • Python SDK
Intermediate Updated 8 min read
On this page
  1. Before you start
  2. Each instance of a multi-instance Android emulator is a device
  3. Create a device group
  4. Give each device its own arguments
  5. Give each device its own proxy
  6. Start all: multi-instance sync on LDPlayer or BlueStacks
  7. Keep per-device state with storage
  8. Webhooks per group
  9. Watch the resource limits of a BlueStacks bot farm
  10. Schedule the group
  11. A complete example
  12. What can go wrong
  13. Port is already in use
  14. One instance runs, the others stay on Connecting
  15. Templates match on one instance only
  16. Every instance shares one counter

One macro, five accounts, one click: that is multi-instance Android emulator farming. Macro Automation Studio (MAS) treats every emulator instance as a separate device. A device group then runs the same macro on all of them, with per-device arguments, a proxy each, and storage that never mixes counters. This guide builds that setup on BlueStacks, LDPlayer, MuMu Player or MEmu. It is for anyone who runs more than one account.

Before you start

  • MAS is installed and signed in with a plan whose device count covers the instances you want to run at once. See Billing.
  • The emulator’s multi-instance manager has created the instances and each one has ADB enabled. The BlueStacks, LDPlayer, MuMu Player and MEmu guides show where the setting and the port live.
  • Every instance uses the same resolution and DPI as the one you captured templates on.
  • The macro works on one instance from the Code Editor. See the loops guide.

Each instance of a multi-instance Android emulator is a device

An emulator instance listens for adb on its own local TCP port. MAS finds instances by scanning running processes and keeping the ports inside each emulator’s known range. Two BlueStacks windows therefore show up as two entries in the Port list. The device is the port; the group is the list of ports.

EmulatorPorts MAS accepts
BlueStacks5555 to 8500
LDPlayerOdd ports from 5555 to 5599
MuMu PlayerOdd ports 5555 to 5599, 7555, 16384 to 16576, 26624 to 27264
MEmu21503 and every port ending in 3 above it, below 30000

Keep every instance open while you add it; a stopped instance has no port to find. The Devices page has the full discovery rules.

Create a device group

  1. Open Device Groups and click Create New Group.
  2. Enter a Name, keep the type Local, and click Create Group.
  3. Open the group and click Add Device.
  4. Enter a Device Name that says which account it is, for example farm-alt-2.
  5. Pick the instance’s port under Port. Click Refresh if it is missing, or choose Custom Port and type it.
  6. Click Add Device and repeat for every instance.
  7. On each device card, choose the macro in the Macro selector.

A port can belong to one device only. Adding it twice fails with “Port is already in use”. See Device groups.

Give each device its own arguments

Most bots need one value that differs per account: a name, a target, a number of rounds. There are two ways to pass it.

Settings profiles (no code changes). If the macro has an argument form from the UI Builder, each device card has Edit Arguments. The dialog carries a Profile bar: enter values, choose Save current values as a new profile, name it and click Create profile. A device follows one profile, and editing the profile updates every device that follows it on its next run. “Default” always exists and cannot be deleted. See Settings profiles and UI Builder.

The port in code. The script can ask which instance it is on and pick its own settings. This needs no form and is what the example at the end does.

python
import mas

port = mas.get_current_device_port()   # 5555, 5557, 5559 ...

Use profiles when the values change often or someone else runs the bot; use the port when the mapping is fixed and lives with the code.

Give each device its own proxy

Accounts that share an IP share a fate. MAS attaches one proxy per device and routes that instance’s traffic through it.

  1. Open Proxies. Under Add a proxy, fill in the Gateway account tab, or the Another provider tab for a proxy of your own (http, https or socks5), and click Add & test.
  2. Wait for the test. It confirms the proxy answers and detects its country; Test repeats it later.
  3. Click Assign and choose the device, or open the device card in Device Groups and pick the proxy under Proxy.

The device card shows the proxy state on every run, and run webhooks carry proxy_exit_ip and proxy_country. A device holds one proxy; attaching another replaces it. See Proxies.

Start all: multi-instance sync on LDPlayer or BlueStacks

Click Start All at the top of the group. MAS starts the devices one after another, half a second apart, each with its own run, its own log and its own exit code. A device without a macro is skipped with an error and the rest continue. Stop All ends every run in the group. The Logs tab on each card shows that device only, and the Dashboard tab shows its runtime UI if the macro has one.

Keep per-device state with storage

mas.save, mas.retrieve and mas.clear are keyed by this computer, the device’s port and a task name. Two instances running the same macro therefore keep separate counters with no extra code. To add them up, mas.retrieve_all(task_name) returns one entry per port with machine_id, port, data and updated_at.

python
TASK = "collector"

mine = mas.retrieve(TASK)                          # this instance only
everyone = mas.retrieve_all(TASK)                  # every instance on this computer
total = sum(entry["data"].get("done", 0) for entry in everyone)

See Storage.

Webhooks per group

To be told when any account finishes or fails, add an endpoint on the Webhooks page, choose the events under Send me, and under Send me events from pick Specific groups and tick the group. Devices you add to the group later are covered too. Each event carries device_name, device_port, group_id, status and exit_code, so one endpoint can tell the accounts apart. See Webhooks.

Watch the resource limits of a BlueStacks bot farm

Every instance is a full Android system. Before you scale, check three things:

  • CPU and RAM per instance. Each instance takes the cores and memory set in the emulator’s instance settings. Add them up and leave headroom for MAS, which runs template matching and OCR for every instance on the same machine. When taps start landing late, you are over.
  • Devices on your plan. A plan is sized by how many of your own devices can run at the same time. Start All runs as many as the plan allows; see Billing and the pricing page.
  • Scheduled jobs. The Scheduler runs up to 20 jobs at once, one per port.

Fewer instances running cleanly beat more instances missing templates.

Schedule the group

The Scheduler targets one port per job, so a group schedule is one job per device.

  1. Open Scheduler and click Create New Schedule.
  2. Pick the Macro, click Scan under Emulator Port and choose the first instance.
  3. Set Date, Time and Recurrence, then click Create Schedule.
  4. Repeat for each port. Give the jobs the same time; different ports never collide.

Scheduled runs use the arguments saved on each device card, or the profile the device follows, so per-account values carry over. The app must stay open. See Scheduler and the loops guide.

A complete example

The macro reads its port, picks the account’s settings, keeps a per-port counter, and logs the total across every instance at the end. Replace the ports and image IDs with yours.

python
import random
import sys
import time

import mas

images = mas.images({
    "collect": 501,
    "close_popup": 502,
})

SETTINGS = {   # per instance, keyed by the emulator's adb port
    5555: {"account": "main", "rounds": 20},
    5557: {"account": "alt-1", "rounds": 10},
    5559: {"account": "alt-2", "rounds": 10},
}
DEFAULT = {"account": "unknown", "rounds": 5}
TASK = "collector"
TIME_BUDGET_S = 15 * 60


def main():
    port = mas.get_current_device_port()
    settings = SETTINGS.get(port, DEFAULT)
    account = settings["account"]
    mas.log(f"Port {port}: account {account}, {settings['rounds']} rounds")

    done = mas.retrieve(TASK).get("done", 0)
    started = time.monotonic()
    misses = 0

    while done < settings["rounds"]:
        if time.monotonic() - started > TIME_BUDGET_S:
            mas.log(f"{account}: time budget reached", level="warning")
            break
        popup = mas.find_object(images.close_popup)
        if popup:
            mas.click(popup.x, popup.y, delay_ms=800)
            continue
        button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
        if button is None:
            misses += 1
            if misses >= 5:
                mas.log(f"{account}: five misses, giving up", level="error")
                sys.exit(2)
            continue
        misses = 0
        mas.click(button.x, button.y, delay_ms=1000)
        done += 1
        mas.save(TASK, {"done": done, "account": account})
        time.sleep(random.uniform(0.8, 2.5))

    total = sum(entry["data"].get("done", 0) for entry in mas.retrieve_all(TASK))
    mas.log(f"{account}: {done} rounds done; {total} across all instances")
    mas.webhook("account.finished", {"account": account, "port": port, "done": done})


if __name__ == "__main__":
    main()

mas.webhook sends a custom.account.finished event to endpoints subscribed to custom events; leave the line out if you have none. When the mapping grows, move it into an argument form and read args.<tab>.<key> instead of the dictionary.

What can go wrong

Port is already in use

Another device in one of your groups owns that port. Each port can be added once; remove the old entry or pick the other instance’s port.

One instance runs, the others stay on Connecting

The other instances have ADB off, are still booting, or listen on ports outside the emulator’s range. Open each instance’s settings, confirm ADB and the port, and click Refresh in Add Device. If the port sits outside the table above, use Custom Port. See ADB troubleshooting.

Templates match on one instance only

That instance has a different resolution or DPI. Template matching is pixel based; set every instance to the capture resolution in the emulator’s instance settings and restart it. The image recognition guide explains the rest.

Every instance shares one counter

The script passes an explicit port or machine_id to mas.save, or uses a file on disk instead of storage. Call mas.save(TASK, data) with the defaults; the current port is added for you.

No automation tool is 100% risk-free, so automate responsibly and at your own discretion.

Next steps

Related pages

Was this page helpful?

Questions? Ask in Discord