oleander-whirlwind
10/07/2025, 11:49 AMMarvin
10/07/2025, 11:49 AMMarvin
10/07/2025, 11:53 AMrun_deployment("FlowName/DeploymentName", ...) for each.
Two common ways to ensure the right Python per workflow
Option A — Docker work pools (recommended for strong isolation)
1) Create two Docker work pools:
prefect work-pool create py311-pool --type docker
prefect work-pool create py312-pool --type docker
Note: install the extra if needed: pip install 'prefect[docker]'.
2) Build/push two images that contain your code/deps for each Python version (e.g., <http://ghcr.io/acme/app:py311|ghcr.io/acme/app:py311>, `ghcr.io/acme/app:py312`).
3) Deploy each flow to the matching pool and image. With the Python API:
from prefect import flow
@flow
def flow_py311(...):
...
@flow
def flow_py312(...):
...
# Deploy the 3.11 flow
flow_py311.from_source(
"<https://github.com/your-org/your-repo>",
entrypoint="flows/py311_flow.py:flow_py311",
).deploy(
name="prod",
work_pool_name="py311-pool",
job_variables={"image": "<http://ghcr.io/acme/app:py311|ghcr.io/acme/app:py311>"},
)
# Deploy the 3.12 flow
flow_py312.from_source(
"<https://github.com/your-org/your-repo>",
entrypoint="flows/py312_flow.py:flow_py312",
).deploy(
name="prod",
work_pool_name="py312-pool",
job_variables={"image": "<http://ghcr.io/acme/app:py312|ghcr.io/acme/app:py312>"},
)
You can also use the CLI from each project root:
prefect deploy ./flows/py311_flow.py:flow_py311 --name "flow_py311/prod" --pool py311-pool --job-variable image=<http://ghcr.io/acme/app:py311|ghcr.io/acme/app:py311>
prefect deploy ./flows/py312_flow.py:flow_py312 --name "flow_py312/prod" --pool py312-pool --job-variable image=<http://ghcr.io/acme/app:py312|ghcr.io/acme/app:py312>
4) Start one worker per pool (e.g., on machines with Docker):
prefect worker start -p py311-pool
prefect worker start -p py312-pool
Option B — Process workers + venvs (simple local isolation)
- Create two virtual environments:
- venv311 with Python 3.11 + deps
- venv312 with Python 3.12 + deps
- Create two process work pools:
prefect work-pool create py311-pool --type process
prefect work-pool create py312-pool --type process
- From each venv, start a worker for its pool:
- In venv311: prefect worker start -p py311-pool
- In venv312: prefect worker start -p py312-pool
- Deploy each flow to its matching pool (similar to the Docker example, just omit job_variables.image).
Orchestrator flow that triggers both
Use run_deployment to kick off the two deployments. You can wait for them to finish or fire-and-forget.
Wait for both to finish (concurrently):
from prefect import flow
from prefect.deployments import run_deployment
import asyncio
@flow
async def orchestrator():
r1, r2 = await asyncio.gather(
run_deployment("flow_py311/prod", parameters={"x": 1}),
run_deployment("flow_py312/prod", parameters={"y": 2}),
)
return {"py311_state": r1.state.name, "py312_state": r2.state.name}
Fire-and-forget (return immediately after submitting the runs):
@flow
async def orchestrator_fire_and_forget():
r1 = await run_deployment("flow_py311/prod", parameters={"x": 1}, timeout=0)
r2 = await run_deployment("flow_py312/prod", parameters={"y": 2}, timeout=0)
return {"py311_id": r1.id, "py312_id": r2.id}
Notes and tips
- run_deployment name is "flow_name/deployment_name". It returns a FlowRun.
- By default, as_subflow=True, so the triggered runs appear as subflows under your orchestrator in the UI (recommended).
- Waiting behavior:
- timeout=None waits indefinitely for completion.
- timeout=0 returns immediately (fire-and-forget).Marvin
10/07/2025, 11:53 AMpoll_interval controls how often completion is checked while waiting (default 5s).
Helpful docs
- Run deployments programmatically: Run deployments
- API reference for `run_deployment`: prefect.deployments.flow_runs.run_deployment
- Deployments and work pools overview: Deployments concept
Questions to tailor this for you
- Which infrastructure do you want to run on (local process, Docker, Kubernetes, something else)?
- Do you want the orchestrator to wait for both workflows to finish, or just trigger them and move on?
- Are your flows in one repo or separate repos? And do you already have Docker images or prefer venvs?
If you share those details, I can provide a concrete, cut-and-paste config for your setup.oleander-whirlwind
10/07/2025, 1:30 PMMarvin
10/07/2025, 1:30 PMMarvin
10/07/2025, 1:32 PMjob_variables={"image": "repo:tag"} — that’s a plain string and totally fine.
- Image data (e.g., a numpy array): don’t pass this directly in `parameters`; it must be serialized or referenced.
Ways to fix it
1) Prefer passing a reference (best for large data)
- Upload the image to storage (S3/GCS/Azure/local path) and pass a URI/path/id.
await run_deployment(
"flow_py311/prod",
parameters={"image_uri": "<s3://my-bucket/path/to/image.png>"},
job_variables={"image": "<http://ghcr.io/acme/app:py311|ghcr.io/acme/app:py311>"}
)
Then the child flow loads the image from the URI.
2) If the array is small, convert to JSON-friendly types
- Convert to lists (can be large/slow for big arrays):
parameters={"image_array": image_np.tolist()}
3) Serialize to a string (only if you must embed the data; watch size limits)
- Use pickle + base64. Note: only unpickle data you trust.
import base64, pickle
def encode_ndarray(arr):
return base64.b64encode(pickle.dumps(arr)).decode("utf-8")
def decode_ndarray(s):
return pickle.loads(base64.b64decode(s.encode("utf-8")))
# Orchestrator side
encoded = encode_ndarray(image_np)
await run_deployment(
"flow_py312/prod",
parameters={"image_b64_pickle": encoded},
timeout=0, # optional: fire-and-forget
)
# Child flow side
arr = decode_ndarray(image_b64_pickle)
Also, to set the Docker image for the worker use job_variables, not a parameter:
await run_deployment(
"flow_py311/prod",
parameters={"x": 1},
job_variables={"image": "<http://ghcr.io/acme/app:py311|ghcr.io/acme/app:py311>"}
)
Docs for reference:
- Run deployments: Run deployments
- Serializers API (if you want to roll your own): prefect.serializers
If you share the exact run_deployment(...) call you’re using (especially what’s inside parameters), I can suggest the smallest change to make it work.