Workers
Setup
A worker connects your own machine to workbenches.dev as a self-hosted runner. Runs started on workbenches.dev, from the API or mcp.workbenches.dev, can execute on it instead of in a hosted Daytona sandbox. workbenches.dev stays the orchestrator: the worker reports the same normalized events a cloud run does, so the run page, wait, commands, outcomes, and pull requests work as they do for a cloud run.
Worker time is free. A worker run records no sandbox time and is not checked against your plan's sandbox hours.
A worker runs more than a sandbox can:
- Any runner. OpenCode, Claude Code, and Pi all run on a worker.
- Local-only Workbenches, on its
localruntime, with the machine's own tools, such as Xcode on a Mac. - Members' own sign-ins, such as a Claude or ChatGPT subscription, instead of only the organization's OpenRouter key. See Member sign-ins.
It does not offer live previews yet.
A worker needs a workbenches.dev account. Nothing else in wb depends on it. wb worker supports macOS and Linux; on Windows wb worker start fails with wb worker is not supported on Windows yet.
Read the trust model before sharing a worker: every run executes as the machine's account, with no sandbox.
Start a worker#
Everything is done from a shell on the host, usually over SSH. No desktop session is needed.
wb login # or wb login --org acme
wb worker start --name miniWithout a login, the first start fails with Sign in first with wb login.
start on a machine that is not yet a worker registers it, with your own wb login for the default organization or the one --org names. You must be a member of that organization, and you become the worker's owner. It stores a worker key in $WORKBENCH_HOME/worker/worker.json with mode 0600. That key can only connect as this worker; your login key never reaches it. The worker keeps using the API it registered with.
start then installs and starts a background service and returns:
- macOS: a launchd agent in your user's background domain (
user/<uid>), - Linux: a systemd user service with lingering enabled,
so the worker keeps running after the SSH session ends. Once it connects it prints:
Worker mini is online for acme. 1 slot, runtimes docker, local. Follow it with: wb worker logs -for that it started and is still connecting.
Options#
| Option | Effect |
|---|---|
--name <name> |
The worker's name, to register this machine. On a registered machine it may be left out. |
--org <slug> |
The organization to register with. Defaults to your default organization. |
--runtimes <list> |
Narrow the runtimes it offers, such as --runtimes docker to turn local runs off. |
--slots <n> |
How many runs it takes at once. Defaults to 1. |
--runtimes and --slots are remembered for the next start. On a registered machine, a different name fails with This machine is already the worker mini. Run wb worker remove first.
The service runs with the PATH of the shell that started it, so Docker and runner CLIs are found the same way they are in that shell. A plain ssh host wb worker start gets a non-login shell with a short PATH; start it from an interactive login shell, such as zsh -lic 'wb worker start', so it finds what you installed.
Runtimes#
A worker offers every runtime the machine can run. It checks both each time it starts.
| Runtime | Offered | What a run gets |
|---|---|---|
docker |
When Docker is reachable as the worker starts | One container per run from the Workbench's own image, sized by its requirements. The host needs only wb and Docker. |
local |
Always | The run executes on the host as the account that started the worker, with this machine's tools and Docker, and the starting member's directory as its home. |
Nobody picks a runtime when starting a run. workbenches.dev places a run on the first runtime its Workbench declares that the worker offers: local runs on the host, and an entry with an image (docker, e2b, or daytona) runs in Docker.
start fails when the machine can run none of the allowed runtimes.
Send runs to it#
A worker is personal at first: only runs you start go to it. To let every member of the organization use it, share it from its page on workbenches.dev, or with the API (PATCH /v1/workers/:id with { "shared": true }). You can make it personal again the same way.
To run on it, pass worker with its slug to the run MCP tool, or worker in POST /v1/runs. The workers MCP tool and GET /v1/workers list the organization's workers with their slugs.
A run is refused, and the reason shown, when:
| Status | Reason |
|---|---|
| 404 | The organization has no worker with that slug. |
| 403 | The worker is personal to another member: The worker <name> is personal to <owner name>. A run started by a key with no member is refused the same way. |
409 worker_offline |
The worker is not connected. |
422 worker_unsuitable |
It offers no runtime the Workbench declares, its machine does not meet the Workbench's requirements (os, arch, cpu, memory_gb, gpu), or the package is over the 64 MiB a worker downloads. |
The worker itself also rejects a run whose runtime it does not offer, whose Workbench does not declare that runtime, whose requirements it cannot meet, or that arrives with every slot busy. A run waits for a free slot.
A worker reports the GPUs it finds, with each name and, for NVIDIA, its memory, every time it connects. A local run that needs a GPU passes on that discovery alone, since a worker run has no --allow-unchecked-gpu.
On a personal worker, send, steer, queue, and answer commands are refused for anyone but its owner. Any member may cancel.
Credentials for runs#
Before members run on a worker, each connects their own runner sign-ins there with wb connect --worker. Without one, a run uses the provider keys workbenches.dev sends with it. See Member sign-ins.