> ## Documentation Index
> Fetch the complete documentation index at: https://www.activepieces.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Worker Groups

> Reserve dedicated worker capacity for specific projects

By default every worker serves every project from one shared queue. When one busy project floods it, everyone else waits.

**Worker groups** fix that. A group is a named pool of workers with its own dedicated queue. Assign a project to a group and its runs get reserved workers no other project can take.

![Worker groups: projects map to dedicated queues, each backed by its own reserved worker pool, while ungrouped projects share a best-effort queue](https://mintcdn.com/activepieces/HPdB7kMdQC2Eh_kp/resources/diagrams/worker-groups.png?fit=max\&auto=format\&n=HPdB7kMdQC2Eh_kp\&q=85\&s=d55034f69fd423265319a2dabd339aef)

## Floor vs. ceiling

* A **worker group** is a **floor**: capacity reserved for a project that nobody else can use.
* A **soft cap** is a **ceiling**: an optional per-project limit on how much a project can use. See [Manage Concurrency](https://www.activepieces.com/docs/admin-guide/guides/manage-concurrency.md) for details.

Use both together to guarantee capacity for busy projects and cap the rest.

## How it works

A worker joins a group by setting its group ID at startup. `AP_WORKER_GROUP_SCOPE` selects the group's scope:

* **Project** group — `AP_WORKER_GROUP_SCOPE=project` (the default). Assign individual projects to it from **Platform Admin → Operations → Workers → Worker groups**.
* **Flow** group — `AP_WORKER_GROUP_SCOPE=flow`. Pins individual flows to it, regardless of which group (if any) their project is in. See [Assigning flows to a group](#assigning-flows-to-a-group).
* **Platform** group — `AP_WORKER_GROUP_SCOPE=platform` (or the legacy `AP_PROJECT_WORKER=false`). Serves the whole platform's runs; used for dedicated/canary fleets.

A group's scope is fixed by whichever scope its workers connect with — a label can't take both project and flow assignments at once, so give project and flow pools distinct `AP_WORKER_GROUP_ID` values.

```bash theme={null}
# Project group, e.g. 1cpu_machine (AP_WORKER_GROUP_SCOPE defaults to project)
AP_WORKER_GROUP_ID=1cpu_machine

# Flow group, e.g. gpu_flows
AP_WORKER_GROUP_ID=gpu_flows
AP_WORKER_GROUP_SCOPE=flow

# Platform group
AP_WORKER_GROUP_ID=canary
AP_WORKER_GROUP_SCOPE=platform
```

> [!NOTE]
>
> `AP_PROJECT_WORKER` (`true`/`false`) still works as the legacy project-vs-platform toggle, but `AP_WORKER_GROUP_SCOPE` is the current setting and the only way to request the flow scope.

Grouped workers must set:

```bash theme={null}
AP_REUSE_SANDBOX=true               # or false, must be set explicitly
```

> [!WARNING]
>
> Routing runs to worker group queues is done by the project rate limiter, so the app server must run with `AP_PROJECT_RATE_LIMITER_ENABLED=true`. Without it, assigned projects keep using the shared queue and grouped workers stay idle.

Once a project is assigned to a group, its flow and webhook runs are routed to that group's dedicated queue (`project-<group-name>-jobs`) and picked up only by workers carrying the matching `AP_WORKER_GROUP_ID` with scope `project`. If no worker in the group is online, runs wait in the group queue until one connects — they are never executed by shared workers. Changing a project's assignment — to a group, between groups, or back to shared — also moves its waiting runs to the new queue. Projects without a group keep drawing from the shared queue.

A flow pinned to a group works the same way, but on its own queue (`flow-<group-name>-jobs`), picked up only by workers with scope `flow`. **A flow's own pin always wins over its project's group** — pin a flow to send just that flow's runs elsewhere without moving the rest of the project.

## Assigning projects to a group

You can assign a project to a project group in two ways:

* **UI** — go to **Platform admin → Operations → Workers → Worker groups** (`/platform/workers/groups`) and pick the group for each project.
* **API** — set `workerGroupId` on the project via the projects API. Use the bare group name (the same value as the group's `AP_WORKER_GROUP_ID`); pass `null` to unassign and return the project to the shared queue.

The same request also sets `maxConcurrentJobs` — the project's **concurrency limit** (the soft-cap ceiling). It's the maximum number of flow runs the project executes at once:

* **Omit it** (or send `null`) and the project uses the default: `min(plan limit, group slots)` — i.e. it can use the group's full reserved capacity, up to the plan's limit.
* **Set a number** to cap the project below that. The value is clamped to the group's slot count (a project can't run more concurrently than its pool can serve).

```bash theme={null}
curl -X POST 'https://your-instance.com/api/v1/projects/<PROJECT_ID>' \
  -H 'Authorization: Bearer <PLATFORM_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{ "workerGroupId": "1cpu_machine", "maxConcurrentJobs": 2 }'
```

## Assigning flows to a group

Pin individual flows to a **flow** group when only a few flows — not their whole project — need dedicated capacity (more memory, a GPU, a specific region). A pinned flow's runs route to that group even if its project is on the shared queue or assigned elsewhere; unpin it and the flow falls back to its project's group, or the shared queue.

* **UI** — go to **Platform admin → Operations → Workers → Worker groups** (`/platform/workers/groups`), open a flow group, and add flows to it by searching across projects.
* **API** — `POST /v1/worker-groups/assignments/flows` with a platform API key:

```bash theme={null}
curl -X POST 'https://your-instance.com/api/v1/worker-groups/assignments/flows' \
  -H 'Authorization: Bearer <PLATFORM_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{ "flows": ["<FLOW_ID>"], "workerGroupId": "gpu_flows" }'
```

Pass `workerGroupId: null` to unpin flows and return them to their project's group. If workers are currently online under that group label with the `project` scope, assigning a flow to it is rejected — a label serves one scope at a time.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.