- Security or procurement asked where agent commands run, and you’d rather answer with an image, an egress allowlist, and a per-connection log than with a fleet you operate.
- Your environment doesn’t fit a Cloud Agent build: a custom base image, a heavy toolchain, or a large checkout you want to keep warm across follow-ups.
- Agents write a real share of your pull requests now, and idle machines have become the cost problem.
How it works
- Cursor runs the agent loop and a per-team queue of pending pool requests. Each worker opens one outbound HTTPS connection to Cursor, and Cursor sends tool calls over it. Nothing connects into your infrastructure.
- You run Cursor’s
agent worker controlleron a host you manage, pointed at a spawn hook from this guide. The controller claims requests from the queue and runs the hook once per claim. - Superserve provides the workers. The spawn hook creates a sandbox and starts
agent workerinside it under the worker id Cursor assigned to the claim. A monitor process recycles the sandbox when the worker exits.
Prerequisites
- A Superserve account and API key
- A Cursor Enterprise plan with Self-Hosted Machines enabled by a team admin
- A Cursor service-account API key. Personal, team, and organization keys cannot start pool workers.
- A Linux or macOS controller host with the Cursor CLI, outbound HTTPS, and Node.js 22+ or Python 3.12+
Configure your Cursor team
A team admin enables three things in the Cursor dashboard:- Allow Self-Hosted Machines under Cloud Agents → Self-Hosted. This adds the pool picker to Cloud Agent creation.
- GitHub token minting for self-hosted workers, on the same page. The worker starts with
--clone-git-repos, which asks Cursor for a repo-scoped token at claim time. Without minting, workers connect but cannot check anything out. - The Cursor GitHub App under Settings → Integrations, connected at the team level with access to every repo the pool will serve. Minted tokens inherit their permissions from the app.
Build the worker template
Build a template with the Cursor CLI,git, and a /workspace directory. Sandboxes created from it boot in under 50ms with the CLI already on PATH, so nothing is downloaded at claim time.
agent --version step fails the build immediately if the CLI download or install broke, rather than at the first claim.
Run the controller
Clone the reference implementation, add both keys to.env, and build the template:
.env themselves, but the controller is a Cursor binary and does not, so export it into that shell first.
--pool registers the pool with Cursor, so superserve appears in the pool picker as soon as the controller connects. Any name works except default, which does not allow --clone-git-repos.
The spawn hook
The controller runs the hook once per claim withCURSOR_AGENT_WORKER_ID, CURSOR_POOL, CURSOR_REQUEST_ID, and the request’s repo fields in its environment. The hook creates a sandbox tagged with the worker id and launches the worker:
- Finds or creates a sandbox for the worker id. A paused sandbox with the same tag is resumed rather than recreated (see Hibernate with pause and resume).
- Launches the worker detached from the exec call, with
CURSOR_AGENT_WORKER_IDin its environment. The Cursor CLI reads the id from there and connects as the worker the controller already claimed for. - Verifies it stayed up. If the worker is not running a few seconds later, the hook prints the worker log, releases the claim so another worker can take the request, deletes the sandbox, and exits non-zero.
Send a task
In Cursor, create a Cloud Agent for a repo the GitHub App can access and select thesuperserve pool. Slack, GitHub, Linear, and the Cloud Agents API can target the pool the same way.
A cursor-<worker id> sandbox appears in the console, tagged with the worker, pool, and request ids. Open its terminal to watch the worker:
/workspace, serves the session, and stays connected for follow-ups. Once the idle window closes it exits, and the monitor deletes the sandbox on its next pass.
Idle release and cleanup
A worker serves one request and stays attached to it. After a session ends it keeps its connection open forCURSOR_WORKER_IDLE_RELEASE_TIMEOUT seconds - 600 in this guide, an hour by Cursor’s default - so a follow-up reuses the workspace instead of starting cold. When the timer fires the worker exits with code 0. The monitor sweeps every 15 seconds, finds sandboxes whose worker has exited, and deletes them.
Hibernate with pause and resume
Deleting on idle discards the checkout, the dependency install, and any build cache. A follow-up that arrives an hour later starts from an empty/workspace.
Cursor supports hibernation: a pool can declare a reconnect window, and Cursor holds a follow-up for an offline worker instead of reassigning it. Superserve’s pause() fits this directly - it checkpoints the full sandbox - memory, processes, and filesystem - at zero compute cost, and resume() brings it back in under 50ms.
1
Give the pool a reconnect window
Register the pool with
workerReadyTimeoutSeconds. Cursor holds a follow-up for up to that long while the worker’s machine comes back:2
Pause instead of delete
Set
CURSOR_WORKER_HIBERNATE=true in .env. The monitor now pauses a sandbox when its worker exits. SANDBOX_AUTO_DELETE_SECONDS still applies, so sandboxes nobody returns to are deleted after a day by default.3
Wake on follow-up
In hibernate mode the monitor also polls the pool’s pending requests. When Cursor lists a follow-up as claimed by an offline worker and a paused sandbox carries that worker id, the monitor resumes the sandbox and starts a worker under the same id. The follow-up continues on the original workspace.
The spawn hook and the monitor coordinate through sandbox metadata tags
(
cursor.launching, cursor.recycling), and metadata updates have no
compare-and-swap. Each side writes its tag and re-reads before acting, the
launch script holds a lock so two launchers can never start two workers, and
claims are released only after a worker is confirmed gone. What remains is a
window of one API round-trip in which a recycle and a relaunch can overlap;
the outcome in that case is a follow-up that Cursor re-queues, never two
workers on one request.Warm workers
For pools where startup latency matters, run the controller with--warm-idle <count>. It keeps that many idle workers connected ahead of demand and Cursor assigns requests to them directly. The same spawn hook serves this mode; it runs without a request id.
Lock down egress
Sandboxes can reach any public IP by default. A worker needs outbound HTTPS to:api2.cursor.shandapi2direct.cursor.sh- the agent sessiondownloads.cursor.com- CLI self-updatescloud-agent-artifacts.s3.us-east-1.amazonaws.com- screenshot and recording uploads- The git host, package registries, and any internal services the agent’s work touches
CURSOR_WORKER_ALLOW_OUT to a comma-separated list of hosts and CIDRs. The spawn hook creates sandboxes that allow those, plus the sandbox’s DNS resolvers (1.1.1.1/32 and 8.8.8.8/32, without which nothing resolves) and *.superserve.ai (which the SDK needs to reach the sandbox), and deny everything else. Write single IPs as /32; the hook does this for you if you leave the suffix off. See Network rules for the format and the network log for every HTTP and HTTPS connection a worker attempted, allowed or blocked.
No inbound ports are required. The worker connects out to Cursor, and Superserve reaches the sandbox through its own control plane.
Git access comes from tokens Cursor mints per run. There is no personal
access token in
.env, in the template, or in the sandbox. Use a dedicated
service account per pool so a compromised worker can only claim that pool’s
work.Configuration
The scripts read these from.env:
The controller sets
CURSOR_AGENT_WORKER_ID, CURSOR_REQUEST_ID, and CURSOR_WORKER_NAME on each spawn. Leave them out of .env.
Troubleshooting
Requests sit in the queue and nothing spawns
Requests sit in the queue and nothing spawns
Check that the controller is running, that its
--pool matches the pool users select, and that CURSOR_API_KEY is a service-account key. The controller logs every claim and spawn; a silent log means it is not seeing requests.The spawn hook reports the worker failed to start
The spawn hook reports the worker failed to start
Read the log tail it printed. Common causes: a key of the wrong type,
--clone-git-repos without GitHub token minting enabled, or the default pool. To run workers that do not clone, set CURSOR_WORKER_CLONE_GIT_REPOS=false.The template build fails on agent --version
The template build fails on agent --version
The build VM could not fetch the CLI from
downloads.cursor.com, or the installed binary did not run on the base image. Stream the build logs to see which.Sandboxes accumulate in the console
Sandboxes accumulate in the console
The monitor is not running. Every sandbox the hook creates carries
cursor.managed=true in its metadata, so Sandbox.list({ metadata: { "cursor.managed": "true" } }) finds them all.Follow-ups start on a fresh sandbox with hibernation on
Follow-ups start on a fresh sandbox with hibernation on
All three pieces are required: the pool registered with
workerReadyTimeoutSeconds, CURSOR_WORKER_HIBERNATE=true, and the monitor running with CURSOR_API_KEY and CURSOR_POOL in its .env.Resources
Reference implementation
Template builder, spawn hook, and monitor in TypeScript and Python.
Cursor Team Pools
Pools, the worker controller, hibernation, and the pending-request API.
Templates
Build reusable images with your toolchains baked in.
Pause, resume, and delete
Checkpoint a sandbox between turns and restore it on demand.