Florent VanDeMoortele
12/23/2025, 5:32 PMMarvin
12/23/2025, 5:33 PMMarvin
12/23/2025, 5:39 PMprefect worker start as a system service on an always-on instance.
- Docker worker on a GCE VM: 2–10s when the image is already pulled.
- Kubernetes worker (GKE): 10–30s to schedule a pod and start the container.
References
- Prefect GCP Cloud Run worker (v1): CloudRunWorker code
- Prefect GCP Cloud Run worker (v2): CloudRunWorkerV2 code
- GCP docs: Cloud Run Jobs overview: Cloud Run Jobs
If you share:
- How often your flows run (e.g., every minute vs hourly)
- Your acceptable startup latency target
- Whether you prefer fully serverless vs managing a tiny VM
…I can recommend the best worker type and outline the setup.Florent VanDeMoortele
12/23/2025, 5:41 PMMarvin
12/23/2025, 5:41 PMMarvin
12/23/2025, 5:48 PMgcloud auth configure-docker <region>-docker.pkg.dev)
- Pre-pull your flow image and keep it cached locally
- Use a Docker work pool; start a worker on the VM; deploy your flow to that pool with image_pull_policy set to if_not_present or never
Step-by-step
1) Create a Docker work pool (once)
prefect work-pool create "gce-docker" --type docker
2) Optionally set the pool’s base job template to avoid pulls on every run
- Fetch the template:
prefect work-pool get-default-base-job-template --type docker --file docker-pool.json
- Edit docker-pool.json and set:
- "image": "REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG"
- "image_pull_policy": "if_not_present" (or "never" if you’ll always pre-pull)
- Apply:
prefect work-pool update "gce-docker" --base-job-template docker-pool.json
3) On the VM, install requirements and pre-pull the image
pip install "prefect[docker]"
gcloud auth configure-docker REGION-docker.pkg.dev
docker pull REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG
4) Start a Prefect worker on the VM (as a service)
- Minimal start:
export PREFECT_API_URL="<https://api.prefect.cloud/api/accounts/<ACCOUNT_ID>/workspaces/<WORKSPACE_ID>>"
export PREFECT_API_KEY="<YOUR_API_KEY>"
prefect worker start -p "gce-docker" -n "gce-docker-1"
- Optional systemd unit:
[Unit]
Description=Prefect Docker Worker
After=docker.service
Requires=docker.service
[Service]
Environment=PREFECT_API_URL=<https://api.prefect.cloud/api/accounts/<ACCOUNT_ID>/workspaces/<WORKSPACE_ID>>
Environment=PREFECT_API_KEY=<YOUR_API_KEY>
ExecStart=/usr/local/bin/prefect worker start -p gce-docker -n gce-docker-1
Restart=always
[Install]
WantedBy=multi-user.target
5) Deploy your flow to this pool using your existing image
- If your image already contains the flow code at the right path:
from prefect import Flow
flow = Flow.from_source(
source=".", # entrypoint path must exist inside the image
entrypoint="app/flow.py:main_flow",
)
flow.deploy(
name="prod",
work_pool_name="gce-docker",
image="REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG",
build=False, # image already built
push=False, # image already pushed
)
Tips to reliably hit <10s
- Keep the image small and pre-pulled on the VM
- Set image_pull_policy to if_not_present (or never if you manage pulls yourself)
- Avoid runtime git clone or pip install in the container’s entrypoint
- Keep VM and Artifact Registry in the same region
- Use a fixed tag or digest to avoid unexpected pulls
Alternative if you don’t need containers
- Process worker on a GCE VM is the fastest (<1s), but only if you can install your code and deps on the VM directly (no Docker). For your current containerized setup, Docker worker is the best fit.
If you want, I can tailor a systemd unit + Terraform snippet for the VM (with a MIG) and a minimal Dockerfile to keep launch times in the 2–5s range.Florent VanDeMoortele
12/24/2025, 9:00 AM- Keep everything regional:
- Store the image in Artifact Registry in the same region as the job
- Ensure the job’s service account has Artifact Registry Reader to avoid auth retries
- Avoid optional features that add provisioning time unless needed (VPC connectors, large memory, etc.)
Marvin
12/24/2025, 9:00 AMMarvin
12/24/2025, 9:02 AMgcloud artifacts repositories list --location=REGION
- Create a regional repo (if needed):
gcloud artifacts repositories create REPO \
--repository-format=docker \
--location=REGION \
--description="Prefect images"
- Tag and push your image to the regional registry:
docker tag IMAGE:TAG REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG
docker push REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG
Docs: Artifact Registry: Store Docker images
b) Configure your Prefect Cloud Run v2 work pool to use that same region and image
- Create or update a Cloud Run v2 work pool:
prefect work-pool create "cr-v2" --type cloud-run-v2
# or update existing
prefect work-pool get-default-base-job-template --type cloud-run-v2 --file cr.json
- Edit cr.json “variables” to set region and image (and keep other settings minimal):
{
"variables": {
"region": "REGION",
"image": "REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG",
"service_account_name": "<mailto:my-sa@PROJECT.iam.gserviceaccount.com|my-sa@PROJECT.iam.gserviceaccount.com>",
"cpu": "1000m",
"memory": "512Mi",
"vpc_connector_name": null, // leave null/omit unless you need it
"max_retries": 0,
"timeout": 600,
"env": {}
}
}
- Apply the template:
prefect work-pool update "cr-v2" --base-job-template cr.json
Reference: CloudRunWorkerV2 variables and job template
2) Ensure the job’s service account has Artifact Registry Reader
- Create a service account (or pick an existing one) to run the job:
gcloud iam service-accounts create prefect-cr-sa \
--display-name="Prefect Cloud Run Jobs"
- Grant Artifact Registry Reader at the repository level (preferred) or project level:
Repository-level:
gcloud artifacts repositories add-iam-policy-binding REPO \
--location=REGION \
--member="serviceAccount:prefect-cr-sa@PROJECT.iam.gserviceaccount.com" \
--role="roles/artifactregistry.reader"
Project-level (broader):
gcloud projects add-iam-policy-binding PROJECT \
--member="serviceAccount:prefect-cr-sa@PROJECT.iam.gserviceaccount.com" \
--role="roles/artifactregistry.reader"
- Set that service account on the work pool template (as shown above: service_account_name).
Docs:
- Artifact Registry IAM roles
- Cloud Run Jobs overview
- Cloud Run service identity
3) Avoid optional features that add provisioning time
- VPC connector: only set vpc_connector_name if you must reach private resources. Leaving it null removes the block and speeds up provisioning.
Docs: Cloud Run VPC connectors
- Memory/CPU: keep to the minimum your flow needs (e.g., memory “512Mi”, cpu “1000m”). Larger resources can slow scheduling/startup.
- Extras: don’t set Cloud SQL integrations or other add-ons unless required. In the default Cloud Run v2 template, keep cloudsql_instances empty.
Important note on startup time
These changes reduce cold-start overhead, but Cloud Run Jobs can still take >10s depending on GCP scheduling and image pull. If <10s is a hard requirement, the fastest reliable path is a Docker worker on an always-on GCE VM with your image pre-pulled. I’m happy to share a ready-to-use systemd unit and minimal VM setup for that if you want.
If you share your target region and repo/image names, I can draft a complete cr.json template for your work pool.Florent VanDeMoortele
12/24/2025, 9:44 AMMarvin
12/24/2025, 9:44 AMMarvin
12/24/2025, 9:46 AMprefect work-pool create "gce-docker" --type docker
2) Configure the pool’s base job template (so we don’t pull on every run)
- Dump the default template:
prefect work-pool get-default-base-job-template --type docker --file docker-pool.json
- Edit docker-pool.json:
- Set "image" to your Artifact Registry image (with a pinned tag or digest)
- Set "image_pull_policy" to "IfNotPresent" (or "Never" if you’ll always pre-pull)
Example snippet:
{
"variables": {
"image": "REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:1.2.3",
"image_pull_policy": "IfNotPresent",
"env": {}
}
}
- Apply:
prefect work-pool update "gce-docker" --base-job-template docker-pool.json
3) Provision a small GCE VM in the same region as your Artifact Registry
- e2-small/e2-medium is usually enough. Install Docker and Prefect.
- Authenticate Docker to AR and pre-pull your image:
gcloud auth configure-docker REGION-docker.pkg.dev
docker pull REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:1.2.3
4) Start the Prefect worker on the VM
- Set Cloud API auth:
export PREFECT_API_URL="<https://api.prefect.cloud/api/accounts/<ACCOUNT_ID>/workspaces/<WORKSPACE_ID>>"
export PREFECT_API_KEY="<YOUR_API_KEY>"
- Start worker:
prefect worker start -p "gce-docker" -n "gce-docker-1"
- Optional: run it under systemd so it auto-restarts.
5) Deploy your flow using your prebuilt image (no builds)
- In code:
from prefect import flow
@flow
def my_flow():
...
if __name__ == "__main__":
my_flow.deploy(
name="prod",
work_pool_name="gce-docker",
image="REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:1.2.3",
build=False,
push=False,
job_variables={
"image_pull_policy": "IfNotPresent"
}
)
Notes:
- Using a pinned tag or digest avoids unexpected pulls.
- If you set "image_pull_policy": "Never", ensure the image is always pre-pulled during VM boot/update.
Option B (fastest absolute, no containers): Process worker on GCE VM
- Expected startup: ~1s.
- Tradeoff: you install your code and dependencies directly on the VM; no Docker isolation.
- Steps are similar: create a process work pool, install your app on the VM, start a process worker, and deploy without specifying an image.
Optional hardening and ops tips
- Put the worker on a Managed Instance Group and use a startup script that:
- Installs Docker + Prefect
- Runs gcloud auth configure-docker
- Pulls the image
- Starts the worker (systemd)
- Roll out image updates by pushing a new tag/digest and pre-pulling on the VMs before switching deployments.
- Keep the VM and Artifact Registry in the same region to minimize pull time (even if you rarely pull).
If you share:
- Your region
- The exact AR image path
- Preference for Docker vs Process worker
…I can give you a copy-paste systemd unit, a GCE startup script, and a minimal Terraform example to stand this up with image pre-pull and automatic worker start.Florent VanDeMoortele
12/24/2025, 9:48 AMMarvin
12/24/2025, 9:48 AMMarvin
12/24/2025, 9:48 AMprefect work-pool create "gce-docker" --type docker
- Configure the pool’s template to use your image and avoid pulls:
prefect work-pool get-default-base-job-template --type docker --file docker-pool.json
Edit:
- "image": "REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG"
- "image_pull_policy": "IfNotPresent" (or "Never" if you always pre-pull)
Apply:
prefect work-pool update "gce-docker" --base-job-template docker-pool.json
- On a small GCE VM in the same region as AR:
- gcloud auth configure-docker REGION-docker.pkg.dev
- docker pull REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG
- Start the worker:
export PREFECT_API_URL="<https://api.prefect.cloud/api/accounts/<ACCOUNT_ID>/workspaces/<WORKSPACE_ID>>"
export PREFECT_API_KEY="<YOUR_API_KEY>"
prefect worker start -p "gce-docker" -n "gce-docker-1"
- Deploy with your prebuilt image and no build:
flow.deploy(
name="prod",
work_pool_name="gce-docker",
image="REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG",
build=False,
push=False,
job_variables={"image_pull_policy": "IfNotPresent"}
)
Cheapest (if you can drop containers)
- Why: No Docker overhead; run on a tiny preemptible VM (60–70% cheaper). Startup ~1s.
- How:
- Package your code as a normal Python app on the VM (venv/uv/pip).
- Create a Process work pool:
prefect work-pool create "gce-process" --type process
- Start a worker:
export PREFECT_API_URL=...
export PREFECT_API_KEY=...
prefect worker start -p "gce-process" -n "gce-process-1"
- Deploy without an image:
flow.deploy(name="prod", work_pool_name="gce-process", build=False)
- Cost tips: use e2-micro/e2-small or t2a-standard-1 (ARM) if your stack supports ARM; consider preemptible (spot) to cut cost further. Keep VM and data in the same region.
If containers are required and you want “cheapest”
- Use the Docker worker on a preemptible e2-micro/e2-small (or ARM t2a) VM.
- Pre-pull the image; set image_pull_policy to IfNotPresent/Never.
Want me to drop in a ready-to-use systemd unit and a GCE startup script for either option? If you share your region and image path, I’ll tailor them.