Ariel Farbman
11/06/2025, 4:12 PMMarvin
11/06/2025, 4:12 PMMarvin
11/06/2025, 4:19 PMfrom prefect.client.orchestration import get_client
async def main():
async with get_client() as client:
wq = await client.create_work_queue(
name="my-queue",
work_pool_name="my-pool", # the pool must already exist
description="Queue created via SDK",
concurrency_limit=10, # optional
priority=1, # optional
is_paused=False, # optional
)
print(wq)
# If running as a script:
# import asyncio; asyncio.run(main())
- Sync
from prefect.client.orchestration import SyncPrefectClient
with SyncPrefectClient() as client:
wq = client.create_work_queue(
name="my-queue",
work_pool_name="my-pool", # the pool must already exist
description="Queue created via SDK",
concurrency_limit=10, # optional
priority=1, # optional
is_paused=False, # optional
)
print(wq)
- Optionally, read a queue by name (useful for idempotent scripts)
from prefect.client.orchestration import SyncPrefectClient
with SyncPrefectClient() as client:
wq = client.read_work_queue_by_name(name="my-queue", work_pool_name="my-pool")
print(wq)
Notes:
- Ensure your environment can talk to your Prefect API (e.g., PREFECT_API_URL and, if using Cloud, PREFECT_API_KEY).
- The work_pool_name you pass must already exist. If you need help creating a work pool via SDK as well, let me know which type (process, Docker, Kubernetes) you want.
Docs for reference:
- Python client (PrefectClient)
- Work pools concept
- REST: Create work queue (for field reference)Ariel Farbman
11/07/2025, 7:14 PMMarvin
11/07/2025, 7:14 PMMarvin
11/07/2025, 7:22 PM--limit flag only controls how many flow runs a single worker process executes concurrently. It is not a server-side limit.
What to check for ECS
1) Are your Docker and ECS runs using the same work pool/queue?
- If your ECS worker is pointing at a different pool (or queue), any concurrency limits you set on the Docker pool won’t apply.
- Verify and compare:
- List pools:
prefect work-pool ls
- Inspect the ECS pool and confirm concurrency limit:
prefect work-pool inspect <ecs-pool-name>
- If needed, set a pool-wide concurrency limit:
prefect work-pool set-concurrency-limit <ecs-pool-name> 2
2) Are you relying on a worker-local limit on Docker but not setting it for ECS?
- If your local Docker worker ran with --limit 1 but your ECS worker runs without a limit, the ECS worker can launch multiple flow runs concurrently.
- Start the ECS worker with a limit as well:
prefect worker start -p <ecs-pool-name> --work-queue <queue-name> --limit 2
3) Are there multiple ECS workers polling the same pool/queue?
- If you’ve got several ECS worker tasks/services running, each with a --limit, your aggregate concurrency across workers could be higher than expected. Server-side pool or deployment concurrency will still cap, but if you relied only on --limit when testing locally, you’ll see more runs in ECS.
- If you want a hard cap across workers, prefer setting the pool’s concurrency limit (server-side) in addition to any worker-local limits.
4) If you’re using tag-based concurrency for tasks
- Confirm your task tags are present in the deployed code and that the concurrency limits exist:
prefect concurrency-limit ls
- When enforced, you’ll see tasks enter an AwaitingConcurrencySlot state in the UI until a slot frees up.
5) If you’re using an ECS “push” work pool instead of a polling worker
- There’s no prefect worker start in push mode; the server launches ECS tasks directly. Worker-local limits won’t apply here. Use a work pool concurrency limit (and/or deployment concurrency) for caps.
If you can share a bit more detail, I can be more precise:
- Which concurrency limit are you relying on: work pool, deployment, or tag-based?
- Are you using an ECS pull worker (prefect worker start -p <ecs-pool>) or an ECS push work pool?
- How many ECS worker replicas are running?
- Pool/queue names for Docker vs ECS, and any --limit you used locally.
Helpful docs:
- Work pools and workers: Work pools and Workers
- Work pool concurrency: Work pool CLI (set-concurrency-limit)
- Tag-based concurrency: Tag-based concurrency limits
- ECS guidance: ECS work pools/workersAriel Farbman
11/07/2025, 7:35 PMAriel Farbman
11/07/2025, 7:37 PMMarvin
11/07/2025, 7:37 PMMarvin
11/07/2025, 7:41 PMconcurrency_limit=1, you can still see multiple flows running at the same time if they come from different queues (e.g., one run from Queue A and one from Queue B).
- If you want a global cap across all flows/queues, set a work pool concurrency limit (server-side cap across the pool) or run the worker with --limit 1 (local cap on a single worker). Pool concurrency is the more robust global cap.
2) The queue already existed without a limit (your code doesn’t update it)
- Your function returns immediately if the queue exists, so any previously-created queue without a concurrency limit will stay that way.
- Add an update when the queue exists:
existing = await client.read_work_queue_by_name(name, work_pool_name=pool_name)
if existing.concurrency_limit != concurrency_limit:
await client.update_work_queue(id=existing.id, concurrency_limit=concurrency_limit)
return existing.id
- You can verify the current value with:
prefect work-queue inspect <queue-name> --pool <pool-name>
3) The ECS worker is pulling from more queues than you expect
- If you do not pass --work-queue, the worker will pull from all queues in the pool, which can increase concurrency across queues.
- Ensure you pin the worker to the intended queues and optionally cap its local concurrency:
prefect worker start -p <ecs-pool> \
--work-queue <queue-A> \
--work-queue <queue-B> \
--limit 1
Note: --limit caps concurrent flow runs for this worker instance only. Use a work pool concurrency limit to cap globally across multiple workers.
4) Version mismatch between local and ECS images
- Make sure your local and ECS environments are running the same Prefect 3.x version. Check with:
prefect version
If ECS is on an older build, update the image and redeploy the worker service.
What I recommend you do next
- Confirm the current queue concurrency values:
prefect work-queue inspect <queue-A> --pool <pool-name>
prefect work-queue inspect <queue-B> --pool <pool-name>
If needed, set them explicitly:
prefect work-queue set-concurrency-limit <queue-A> 1 --pool <pool-name>
prefect work-queue set-concurrency-limit <queue-B> 1 --pool <pool-name>
- Decide on your desired behavior:
- Per-queue cap only (each queue limited independently): keep per-queue limits and ensure your worker pulls only the desired queues.
- Global cap across all flows in the pool: set a work pool–level concurrency limit:
prefect work-pool set-concurrency-limit <ecs-pool-name> 1
This will cap concurrent flow runs across all queues in the pool.
- If you still observe more than 1 concurrent run from the same single queue, that would be unexpected — let me know and we can dig deeper (please share Prefect versions, pool/queue names, and a couple flow run IDs).
Docs for reference:
- Work queues CLI: prefect work-queue
- Work pools and pool concurrency: prefect work-pool
- Worker concepts: Workers
- ECS guidance: ECS work pools/workers
A couple of clarifying questions to ensure we pin it down:
- Are you seeing more than 1 concurrent run from the same queue, or just more than 1 total across different queues?
- What prefect version is running locally vs in your ECS container?Marvin
11/07/2025, 7:41 PMprefect worker start command/flags are you using on ECS?Ariel Farbman
11/07/2025, 8:09 PMMarvin
11/07/2025, 8:09 PMMarvin
11/07/2025, 8:09 PMwork_queue_name that doesn’t exist, Prefect will auto-create the queue with concurrency_limit=None (i.e., unlimited). So if you want a per-queue cap, you must set it explicitly — either right after creation or by updating an existing queue.
A small, idempotent helper you can run before/after deployment to ensure the queue has the limit you expect:
from prefect.client.orchestration import get_client
from prefect.client.schemas.objects import ObjectNotFound
async def ensure_work_queue(name: str, pool_name: str, concurrency_limit: int) -> None:
async with get_client() as client:
try:
wq = await client.read_work_queue_by_name(name, work_pool_name=pool_name)
if wq.concurrency_limit != concurrency_limit:
await client.update_work_queue(id=wq.id, concurrency_limit=concurrency_limit)
except ObjectNotFound:
await client.create_work_queue(
name=name,
work_pool_name=pool_name,
concurrency_limit=concurrency_limit,
)
Suggested flow for CI/CD or infra provisioning:
- Create/update the work queue(s) with the desired concurrency first (using the function above or CLI).
- Then create/update your deployment(s) that reference those queue names.
CLI alternatives if you prefer:
# Set per-queue concurrency
prefect work-queue set-concurrency-limit <queue-name> 1 --pool <pool-name>
# Inspect the current setting
prefect work-queue inspect <queue-name> --pool <pool-name>
Docs:
- Work queue CLI: prefect work-queue
- Work pools (for pool-wide caps if you need a global limit): prefect work-pool
Tip: If you need a global cap across all queues in that pool, add a pool-level concurrency limit as well:
prefect work-pool set-concurrency-limit <pool-name> 1
Shout if you want help wiring this into your deployment script so the order is guaranteed and there’s no race with automations kicking off runs.Ariel Farbman
11/07/2025, 8:11 PMMarvin
11/07/2025, 8:11 PMMarvin
11/07/2025, 8:11 PM