We self-host a diverse set of worker pools that are not being served currently by Cursor’s self-hosted routing rules.
Since we run a monorepo, Cursor’s “environment” based routing doesn’t work at all for our use case since it’s 1-1 with repositories. We run your helm chart in k8s for a general worker pool, and then a few onprem mac minis for building ios. We also set up dev- variants of these worker types to do active development on.
Can you advise on how to setup routing for this use case? We trigger generally from Slack/Linear.
Hey @Troy_Gibb,
There’s a quick reframe here that should unblock you: self-hosted routing isn’t environment-based at all. Environments only configure Cursor-managed cloud VMs. Self-hosted worker pools route purely on labels. Every request carries an auto-derived repo=<owner/repo> (from each worker’s git remote) plus an optional pool=<name>.
For a monorepo that’s actually the good news. Since all your fleets check out the same repo, they all carry the same repo= label, so the repo alone can’t tell them apart. The thing that distinguishes them is the pool name.
1. Give each fleet its own pool name with --pool-name (or CURSOR_WORKER_POOL_NAME in the helm chart / pod env):
2. Pick the fleet at trigger time with pool=<name>:
Slack: @Cursor pool=ios-minis <prompt>
Linear: put pool=ios-minis in the text of the triggering @Cursor comment. It’s read only from that comment, not from the issue description and not from issue/project labels. That’s an intentional boundary so a preset label can’t silently reroute someone else’s run onto your infrastructure.
3. One thing to watch on a monorepo: if you opt into self-hosted without a pool= (for example just self_hosted=true), the run only needs to match repo=, so any of your pools serving that repo can claim it. An iOS job could land on the k8s pool. On a shared-repo setup, always name the pool.
The piece that isn’t there yet is routing to the right pool without typing pool= every time (a repo or team-level default, or inferring the pool from a Linear label or assignment). That’s exactly the ask in the thread you linked, so it’s worth keeping that conversation consolidated there where it carries the most weight with our team.
If you paste your agent worker command and a sample Slack/Linear trigger, I’m happy to sanity-check the exact setup.
Thanks for the info! That said, I’ve noticed inconsistent pool assignment. Per your docs:
When --pool-name is omitted, the worker joins the default pool.
There is one main pool we setup, which we want to be the default (the k8s one) where the pool name is omitted per the text above. Then we have the ios pool, and both their dev variants.
The behavior we want here is for when someone tags cursor in either linear/slack without a pool reference, it should route to this default pool and not to any of the other pools. This behavior seems like a pretty reasonable expectation since we don’t expect users to know the underlying infrastructure serving their request.
However in practice, we see cursor routing to our -dev variants and the ios pool. We have no visibility into how these routing requests were determined, and so this is leading to a lot of confusion and bad experience with the tool.
What else am I missing here? Overall we’re very excited about this product, however these routing issues are significantly curtailing our efforts to roll it out. For what it’s worth, I also strongly believe that routing should be a LLM-reasoned layer, for instance if a user asks to create an ios feature, they shouldn’t have to specify pool= at all-- that’s just a leaky implementation detail.
You’re not missing a setting; this is expected today. default isn’t a routing fallback: omitting --pool-name only names that pool default. A trigger with no pool= matches on repo= alone, so on a monorepo every pool sharing that repo (default, ios, both -dev) is equally eligible and whichever worker is free claims it. Routing Rules won’t help either, since they only map repos to environments, not pools.
The only deterministic lever today is naming the pool on the trigger (pool=default for the general fleet), or keeping the -dev workers offline when you’re not iterating on them so they drop out of the candidate set.
The real fix, untargeted work landing on a sensible default without typing pool= (ideally inferred from the task), is exactly the gap in the thread you linked, so best to keep it consolidated there.