Shubham
10/17/2025, 4:31 PMMarvin
10/17/2025, 4:31 PMMarvin
10/17/2025, 4:39 PMprefect.deployments.run_deployment).
- If you want your database/CDC tool or another service to just POST an event and let Prefect react declaratively, use a Webhook + Automation.
A quick decision guide
- Use SDK (run_deployment) when:
- Your app is already handling the DB insert trigger
- You want synchronous control, custom logic, and error handling in your app
- You need easy parameter passing and deduplication with idempotency keys
- Use Webhooks + Automations when:
- You prefer “push an HTTP event and forget” without writing app-side API code
- A third-party/CDC tool can POST directly to Prefect
- You want a declarative, event-driven rule (Automation) that runs a deployment when a specific event arrives
Note: Webhooks require Prefect Cloud (publicly reachable endpoint). If you’re not on Cloud, the SDK is the simpler path. Are you on Prefect Cloud or self-hosted Server?
Option 1: Trigger runs from your app (SDK)
- Works well for “on insert, if field == X then run deployment Y with params Z”.
- You can return immediately or wait for completion.
- Use idempotency keys to avoid duplicate runs on retries.
Example:
import os
from prefect.deployments import run_deployment
# Make sure these are set in your environment if you're on Prefect Cloud:
# PREFECT_API_URL and PREFECT_API_KEY
# e.g., <https://api.prefect.cloud/api/accounts/<account_id>/workspaces/<workspace_id>>
def map_field_to_deployment(field_value: str) -> str:
# return "flow_name/deployment_name"
return {
"alpha": "ingest_flow/alpha-deployment",
"beta": "ingest_flow/beta-deployment",
}.get(field_value, "ingest_flow/default-deployment")
def on_row_insert(row: dict):
deployment_name = map_field_to_deployment(row["field_name"])
params = {
"record_id": row["id"],
"payload": row,
}
flow_run = run_deployment(
name=deployment_name, # format: FLOW_NAME/DEPLOYMENT_NAME
parameters=params,
flow_run_name=f"db-insert-{row['id']}",
idempotency_key=f"db-insert:{row['id']}", # dedupe if retried
tags=["db-trigger"],
timeout=0, # return immediately; omit to wait for completion
)
return flow_run
Notes:
- Name format is FLOW_NAME/DEPLOYMENT_NAME. You can also pass a deployment UUID.
- By default, run_deployment waits for the flow run to finish; set timeout=0 to return immediately.
- Use idempotency_key tied to a stable DB identifier to avoid duplicates.
- Docs: Run deployments (SDK)
Option 2: Webhook + Automation (no app code)
- External system POSTs to a Prefect Cloud Webhook URL.
- Webhook turns the request into a Prefect event using a Jinja template.
- An Automation with a “Run Deployment” action fires when the event matches.
Setup sketch:
1) Create a webhook in Prefect Cloud with a template that captures fields from the request body:
{
"event": "db.row_inserted",
"resource": {"prefect.resource.id": "db.table.my_table"},
"payload": {
"id": "{{ body.id }}",
"field_name": "{{ body.field_name }}",
"row": {{ body | tojson }}
}
}
2) Create an Automation (UI or YAML) using a Deployment Event Trigger:
- Match on event name: db.row_inserted
- Optionally filter by payload.field_name
- Action: Run Deployment
- Parameter template:
{
"record_id": "{{ event.payload.id }}",
"payload": {{ event.payload.row | tojson }}
}
Tips:
- You can validate a shared secret by checking a custom header in your template/filters (e.g., only proceed when headers['X-Webhook-Token'] == 'your-secret').
- Webhook URLs are unique; rotate them if needed.Marvin
10/17/2025, 4:39 PMShubham
10/17/2025, 5:47 PMMarvin
10/17/2025, 5:47 PMMarvin
10/17/2025, 5:55 PMprefect work-pool set-concurrency-limit "my-pool" 10
prefect work-pool clear-concurrency-limit "my-pool"
- Docs: Work pools
- Deployment concurrency limit: Limits how many concurrent runs of a single deployment can execute. Extras are enqueued by default.
- In code when deploying:
from prefect import flow
@flow
def my_flow(): ...
my_flow.deploy(
name="db-triggered",
work_pool_name="my-pool",
concurrency_limit=5, # per-deployment limit
)
- Tag-based concurrency (task-level): Throttle tasks sharing a tag across flows.
- CLI:
prefect concurrency-limit create db-trigger --concurrency-limit 20
- Docs: Tag-based concurrency
- Global concurrency limits / rate limiting: Name-based slots you can use in code for cross-flow throttling.
- Docs: Global concurrency limits
- Emergency brake: You can pause a work pool (manually or via an Automation action) to halt new starts while the queue drains.
- Infrastructure: Use resilient infra for workers (e.g., containers with restart policies, Kubernetes/ECS with autoscaling) so a busy queue doesn’t affect process health.
Handling duplicates from webhooks
- If duplicates are a concern, you have two good options:
- Keep using webhooks + Automations, but add filtering/templating to ignore unwanted events and/or pause/resume pools on surges. Note: Automations’ “Run Deployment” action does not currently expose an idempotency key—duplicates will create duplicate runs unless filtered.
- Route through a tiny service and trigger via SDK with idempotency:
from prefect.deployments import run_deployment
run_deployment(
"flow_name/deployment_name",
parameters={"record_id": event_id, "payload": payload},
idempotency_key=f"db-insert:{event_id}",
timeout=0 # return immediately
)
Docs: Run deployments (SDK)
If you share:
- Whether you’re on Prefect Cloud or self-hosted
- Your expected peak rate of inserts/webhooks
- Your worker infrastructure (process/Docker/Kubernetes)Marvin
10/17/2025, 5:55 PMShubham
10/17/2025, 6:25 PMMarvin
10/17/2025, 6:25 PMMarvin
10/17/2025, 6:27 PM@flow. Running it calls your code directly (local process) or as a subflow. It’s your workflow logic, parameters, and tasks.
- Deployment: a saved, versioned “run configuration” for a flow. It points to your code (local path or repo), defines infrastructure via a work pool, default parameters, schedules/triggers, tags, concurrency limits, etc. Deployments live in the Prefect API and can be triggered from the UI, API/SDK, CLI, webhooks, or automations.
Which runs a workflow remotely?
- A deployment does. Remote execution happens when a worker attached to a work pool picks up a run for a deployment and starts it on your chosen infrastructure (process, Docker, Kubernetes, ECS, etc.). Calling a flow function directly just runs it where the code is executing.
Typical remote setup (Prefect 3.x)
1) Create or use a work pool (e.g., Kubernetes, Docker, Process) and start a worker that polls it:
prefect worker start --pool "<your-pool>"
2) Create a deployment for your flow (code or CLI):
- In code:
from prefect import flow
@flow
def my_flow():
...
my_flow.deploy(
name="prod",
work_pool_name="your-pool",
# optionally: cron="0 * * * *", parameters={...}, tags=[...]
)
- Or run prefect deploy in your project and follow prompts (it will help configure storage, pool, etc.).
3) Trigger runs remotely via UI, CLI/SDK, webhooks, or schedules:
from prefect.deployments import run_deployment
run_deployment("my_flow/prod", parameters={"x": 1})
Docs:
- Concepts: Deployments
- How to run deployments: Run deployments
- Work pools/workers: Work pools
If you share your target runtime (process, Docker, Kubernetes, etc.), I can give a minimal deployment example tailored to it.Shubham
10/18/2025, 3:24 AMMarvin
10/18/2025, 3:24 AMMarvin
10/18/2025, 3:29 AMwork_pool_name and work_queue_name on flow runs help with routing/scheduling, but they do not replace a deployment. Without a deployment, there’s no instruction for a worker to fetch and run your code.
- To run remotely, create a deployment first, then create runs from that deployment (via SDK/CLI/API).
Recommended patterns
- One-time (or at startup) create a deployment, then trigger runs dynamically:
from prefect import flow
@flow
def my_flow(x: int = 0):
...
# one-time deployment
my_flow.deploy(
name="dynamic",
work_pool_name="your-pool",
)
# later, trigger runs remotely
from prefect.deployments import run_deployment
run_deployment("my_flow/dynamic", parameters={"x": 42})
- If your code lives in Git (no prebuilt image needed), deploy from source:
from prefect import flow
flow = flow.from_source(
source="<https://github.com/org/repo>",
entrypoint="flows/my_flow.py:my_flow",
)
flow.deploy(name="dynamic", work_pool_name="your-pool")
Routing to “any” pool
- You can target a specific work queue when creating a run from a deployment (run_deployment(..., work_queue_name="...")), but you can’t override the work pool at run time. If you truly need to run on multiple pools, create one deployment per pool (same flow, different work_pool_name).
Docs
- Deployments concept: Deployments
- Run deployments (SDK/CLI/API): Run deployments
- REST: Create run from deployment: POST /deployments/{id}/flow_runs
- REST: Create flow run (general): POST /flow_runs (note: not sufficient for worker execution without a deployment)
If you share your target infrastructure (process, Docker, Kubernetes, etc.), I can give a minimal deployment snippet tailored to it.Shubham
10/18/2025, 3:39 AMMarvin
10/18/2025, 3:39 AMMarvin
10/18/2025, 3:41 AMShubham
10/18/2025, 3:50 AMMarvin
10/18/2025, 3:50 AMMarvin
10/18/2025, 3:53 AMbody, headers, query, method):
{
"event": "db.row_inserted",
"resource": {
"prefect.resource.id": "db.table.my_table",
"op": "{{ body.op|default('insert') }}",
"env": "{{ headers['x-env']|default('prod') }}",
"target": "{{ body.target|default('default') }}"
},
"payload": {{ body | tojson }}
}
2) Create an Automation with an Event Trigger that matches the webhook event (e.g., event == db.row_inserted) and a Run Deployment action. In the action, pass flow parameters by templating from the event:
{
"parameters": {
"record_id": {
"__prefect_kind": "jinja",
"template": "{{ event.payload.id }}"
},
"op": {
"__prefect_kind": "jinja",
"template": "{{ event.resource.op }}"
},
"raw": {
"__prefect_kind": "jinja",
"template": "{{ event.payload | tojson }}"
}
}
}
Routing to different deployments based on webhook fields
Option A: Multiple Automations (no code)
- Define separate Automations with different match filters, each running a different deployment. For example:
- Automation 1: match event.resource.target == 'alpha' → Run Deployment ingest_flow/alpha
- Automation 2: match event.resource.target == 'beta' → Run Deployment ingest_flow/beta
This is the simplest purely declarative approach.
Option B: One Automation + “router” deployment (code)
- Have the Automation always run a small “router” deployment that decides which downstream deployment to run based on the event fields.
- Example router flow:
from prefect.deployments import run_deployment
from prefect import flow
@flow
def router(event_payload: dict):
target = event_payload.get("target", "default")
deployment = {
"alpha": "ingest_flow/alpha",
"beta": "ingest_flow/beta",
}.get(target, "ingest_flow/default")
run_deployment(
deployment,
parameters={"payload": event_payload},
idempotency_key=f"webhook:{event_payload.get('id')}",
timeout=0 # fire-and-forget
)
- In the Automation, pass event.payload to the router parameters (as shown above).
Notes and caveats
- Automations run deployments; they don’t execute bare flows directly.
- The Run Deployment action currently selects a specific deployment; dynamic selection within a single action isn’t supported, hence the “multiple automations” or “router flow” patterns.
- If you need deduplication/idempotency, the router pattern gives you access to idempotency_key via run_deployment(...).
- Webhooks and Automations are Prefect Cloud features.
Docs
- Webhooks concept: Webhooks
- Create a webhook: Create a webhook
- Automations: Creating automations
- Template parameters in actions: Templating action parameters
- Chaining deployments with events: Chaining deployments
If you share the exact webhook payload shape and your target deployments, I can draft the webhook template and either a set of Automations or a router flow tailored to your case.