Florent VanDeMoortele
09/11/2025, 1:43 PMMarvin
09/11/2025, 1:43 PMMarvin
09/11/2025, 1:46 PMPREFECT_API_URL and PREFECT_API_KEY env vars on the API service.
Step 0 — Prereqs
- Prefect Cloud account + API key (Org > Service Accounts or User > API Keys)
- GCP project, Artifact Registry, and a service account with permissions to run Cloud Run Jobs:
- roles/run.developer
- roles/iam.serviceAccountUser
- roles/artifactregistry.reader
- roles/logging.logWriter
- Install packages your flow needs in your execution image
Helpful docs:
- Connect your API/worker to Prefect Cloud: https://docs-2.prefect.io/latest/cloud/connecting/
- Cloud Run Worker (work pools on GCP): https://docs-2.prefect.io/latest/integrations/prefect-gcp/cloud_run_worker/
- Work pools and workers overview (blog): https://www.prefect.io/blog/work-pools-and-workers-deploy-python-securely
Step 1 — Write a flow
# flows/hello.py
from prefect import flow, get_run_logger
@flow
def hello(name: str = "world"):
logger = get_run_logger()
<http://logger.info|logger.info>(f"Hello, {name}!")
Step 2 — Package and create a deployment
You have two common paths in Prefect 2.x:
- Easiest: Use the Prefect Cloud UI “Create deployment” wizard
- Point it at your entrypoint (e.g., flows/hello.py:hello)
- Select your work pool (Cloud Run)
- Provide image or job template settings if prompted
- Save to create the deployment
- Or CLI (varies slightly by Prefect 2.x version; the UI avoids version-specific flags)
- Build and push a container image with your code and dependencies to Artifact Registry
- Create a deployment that references your entrypoint and your Cloud Run work pool
Note: CLI flags differ across 2.x releases. If you prefer CLI over the UI, tell me your exact Prefect 2.x version and I’ll share the precise commands for that version.
Step 3 — Create a Cloud Run work pool and start a worker
- In Prefect Cloud, create a Work Pool of type “Cloud Run”
- Configure region, service account, and default image (or leave defaults if you’ll set them per deployment)
- Start a worker process that points to your work pool; run it on a GCP environment where it has the roles listed above
- The worker just needs outbound internet access and Prefect Cloud credentials via env vars
Docs: https://docs-2.prefect.io/latest/integrations/prefect-gcp/cloud_run_worker/
Step 4 — Implement the API that triggers deployments
- Use Prefect’s Python client in your API to trigger a flow run asynchronously
- Set PREFECT_API_URL and PREFECT_API_KEY as env vars on your API container
FastAPI example:
```
# api/main.py
import os
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from prefect.client.orchestration import get_client
# Ensure these are set in your API service's environment
# PREFECT_API_URL="https://api.prefect.cloud/api/accounts/<ACCOUNT_ID>/workspaces/<WORKSPACE_ID>"
# PREFECT_API_KEY="<YOUR_KEY>"
app = FastAPI()
class TriggerRequest(BaseModel):
deployment_name: str # "flow_name/deployment_name" (as shown in Prefect)
parameters: dict = {} # optional flow parameters
idempotency_key: str | None = None # optional to dedupe requests
@app.post("/trigger")
async def trigger(body: TriggerRequest):
try:
async with get_client() as client:Marvin
09/11/2025, 1:46 PMdeployment_name format is "<flow_name>/<deployment_name>" (exactly as shown in the Prefect UI)
- The call returns immediately with a `flow_run_id`; the actual compute happens on the work pool (Cloud Run worker)
Step 5 — Deploy the API to Cloud Run
- Build and push your API image
- Deploy to Cloud Run and set env vars:
- PREFECT_API_URL and PREFECT_API_KEY for Prefect Cloud
- Grant the API service account only what it needs (typically none of the Cloud Run Job permissions; those belong to the worker)
Step 6 — Operations, security, and scaling tips
- Concurrency controls: Use tags on your deployment and Prefect concurrency limits for rate or concurrency limiting
- Idempotency: Provide idempotency_key from your client to avoid duplicate runs on retries
- Observability: Use Prefect Cloud UI to watch runs, logs, states, and failures
- Permissions separation: Keep the worker’s powerful GCP roles separate from the API’s service account
- Retries: Configure retries in your flows for resiliency
- Parameters validation: Validate/whitelist parameters in the API before passing them to flows
Common pitfalls
- Running the flow inside your API process. Prefer creating a flow run via Prefect so failures/retries/logs are managed outside your API’s lifecycle.
- Missing Prefect Cloud env vars on the API; the client relies on PREFECT_API_URL and PREFECT_API_KEY.
- Worker not running or misconfigured work pool; ensure the worker is started and has GCP permissions.
Would you like me to:
- Generate a minimal Dockerfile for your flow and API?
- Provide the exact CLI commands for your specific Prefect 2.x version?
- Tailor the setup for Cloud Run vs. GKE (Kubernetes) vs. Compute Engine? Which GCP runtime do you prefer?Florent VanDeMoortele
09/11/2025, 2:04 PMMarvin
09/11/2025, 2:04 PMMarvin
09/11/2025, 2:11 PMFlow.from_source(...).deploy(...)
- CLI is one command: prefect deploy (replaces prefect deployment build)
- Docs: Prefect deploy and project config
- Work pools and workers replace agents/infrastructure blocks
- Clear separation: a work pool defines how to run, a worker polls and executes
- CLI is straightforward and consistent: prefect work-pool ..., prefect worker start ...
- Blog: Work pools and workers overview
- Scheduling is built-in on deployments
- Pass interval, cron, or rrule directly into .deploy(...)
- API: prefect.schedules module (Cron, Interval, RRule)
- Better observability and automations
- Cleaner state transitions, artifacts, and powerful, reusable automations
- GA summary: Prefect 3 GA
GCP specifics
- Install the GCP collection for first-class BigQuery, GCS, Secrets, etc.: prefect-gcp docs
- Common run setups:
- Process worker on a GCE VM (simple and cost-effective)
- Kubernetes worker on GKE (great for scale/auto-scaling)
- Docker worker on GCE/GKE nodes
- You can keep code in GitHub (or any Git repo) and deploy directly from source.
Minimal examples
- Deploy straight from GitHub (no storage blocks):
from prefect.flows import Flow
Flow.from_source(
source="<https://github.com/your-org/your-repo>",
entrypoint="flows/my_flow.py:my_flow",
).deploy(
name="prod",
work_pool_name="gke-pool",
cron="0 * * * *",
parameters={"project": "my-gcp-project"},
)
- CLI workflow (verify before suggesting in your scripts)
prefect work-pool create gke-pool --type kubernetes
prefect worker start -p gke-pool
prefect deploy ./flows/my_flow.py:my_flow -n prod -p gke-pool
Notes on migration from 2.x
- Replaced/removed in 3.x:
- Deployment.build_from_flow() → use Flow.from_source(...).deploy(...)
- prefect deployment build → use prefect deploy
- GitHub storage blocks → use .from_source('<https://github.com/...>')
- Work pools/workers replace agents and infrastructure blocks
- Helpful guide: Upgrade agents to workers
If you share how you’re running on GCP today (GKE, Cloud Run, GCE VM) and what’s painful in 2.x (deployments, schedules, storage, retries, etc.), I can map your setup to the simplest Prefect 3 path and give you cut‑and‑paste commands.Florent VanDeMoortele
09/11/2025, 2:16 PMMarvin
09/11/2025, 2:16 PMMarvin
09/11/2025, 2:17 PMFlow.from_source(...).deploy(...) or prefect deploy (no storage blocks or deployment build ceremony).
- Docs: Prefect deploy and project config
- Runtime: workers/work pools replace agents/infrastructure blocks
- Create a work pool (e.g., Kubernetes), start a worker, done. No juggling infra blocks.
- Blog: Work pools and workers overview
- Triggering from your API: unchanged and straightforward
- Your API calls prefect.deployments.run_deployment(...) with params. Same idea as 2.x, but paired with the simpler 3.x deployment/worker model.
Recommended GCP architecture
- API: Cloud Run (or Cloud Functions) that receives the request and immediately triggers a Prefect deployment run.
- Worker: Kubernetes worker on GKE (or a process/ Docker worker on a GCE VM) that actually runs the flow in the background.
- Avoid running the worker on Cloud Run due to request/compute lifecycle limits.
Minimal 3.x workflow
1) Create a work pool and start a worker (example: GKE)
prefect work-pool create gke-pool --type kubernetes
prefect worker start -p gke-pool
2) Deploy code from GitHub
from prefect.flows import Flow
Flow.from_source(
source="<https://github.com/your-org/your-repo>",
entrypoint="flows/my_flow.py:my_flow",
).deploy(
name="prod",
work_pool_name="gke-pool",
parameters={"project": "my-gcp-project"},
)
3) Call from your API (Cloud Run)
import os
from fastapi import FastAPI
from prefect.deployments import run_deployment
# Ensure these env vars are set in Cloud Run:
# PREFECT_API_URL, PREFECT_API_KEY
app = FastAPI()
@app.post("/ingest")
def ingest(payload: dict):
fr = run_deployment(
name="my_flow/prod", # "<flow_name>/<deployment_name>"
parameters={"payload": payload},
# Optional: idempotency_key="request-id-123",
# Optional: flow_run_name="ingest-{some-id}",
)
return {"flow_run_id": str(fr.id)}
How this is harder in Prefect 2.x
- You had to choose and configure infrastructure blocks (e.g., KubernetesJob), then build/apply deployments with prefect deployment build or Deployment.build_from_flow() and wire up storage (GitHub/GCS blocks), then run an agent. It works, but more moving parts and more YAML/blocks to maintain.
Migration notes (2.x → 3.x)
- 2.x only:
- prefect deployment build / Deployment.build_from_flow() and GitHub storage blocks
- 3.x replacement:
- prefect deploy and Flow.from_source(...).deploy(...) (no GitHub storage block needed)
- Upgrade guide: Upgrade agents to workers
- GCP integration overview: prefect-gcp docs
If you share your current 2.x setup (agent type, storage, where the API runs, where workloads run), I can map it to the simplest 3.x equivalent and give you exact commands and snippets.Florent VanDeMoortele
09/11/2025, 2:20 PMMarvin
09/11/2025, 2:20 PMMarvin
09/11/2025, 2:21 PMflow_run_id
- Use idempotency keys to avoid duplicate background work
- Use deployment schedules/triggers when you need timed or event-driven runs
- Add concurrency limits and automations for control and alerts
How this maps to your GCP API use case (what’s “easier” in Prefect 3 versus 2)
- Prefect 3 makes the orchestrated route simpler:
- Define your deployment with Flow.from_source(...).deploy(...) or prefect deploy (no storage blocks or deployment build).
- Run a worker in a GKE cluster or on a GCE VM (workers/work pools replace agents and infra blocks).
- Your Cloud Run (or Cloud Functions) API calls run_deployment(...) and returns immediately.
Minimal example (FastAPI on Cloud Run triggering a background flow run)
# Ensure PREFECT_API_URL and PREFECT_API_KEY are set in Cloud Run
from fastapi import FastAPI
from prefect.deployments import run_deployment
app = FastAPI()
@app.post("/ingest")
def ingest(payload: dict):
fr = run_deployment(
name="my_flow/prod",
parameters={"payload": payload},
idempotency_key=payload.get("request_id"), # optional but recommended
flow_run_name=f"ingest-{payload.get('request_id','')}",
)
return {"flow_run_id": str(fr.id)}
Worker and deployment setup (3.x)
# Create a work pool and start a worker (GKE is a good fit for scale)
prefect work-pool create gke-pool --type kubernetes
prefect worker start -p gke-pool
from prefect.flows import Flow
Flow.from_source(
source="<https://github.com/your-org/your-repo>",
entrypoint="flows/my_flow.py:my_flow",
).deploy(
name="prod",
work_pool_name="gke-pool",
parameters={"project": "my-gcp-project"},
)
Why this is preferable on GCP
- Run your API on Cloud Run, but keep the worker on GKE or a GCE VM to avoid Cloud Run’s request/compute lifecycle constraints.
- You get retries, observability, and concurrency limits out of the box, plus a clean handoff from API to background execution.
Helpful links
- Background tasks article: Prefect 3 background tasks
- Work pools and workers overview: Blog post
- Deployments in 3.x: prefect deploy and project config
- GCP collection: prefect-gcp docs
If you want, I can tailor this to your exact stack (Cloud Run + GKE vs. GCE VM) and provide copy‑paste manifests and commands.