Florent VanDeMoortele
12/22/2025, 5:36 PMMarvin
12/22/2025, 5:36 PMMarvin
12/22/2025, 5:38 PMMarvin
12/22/2025, 5:38 PMFlorent VanDeMoortele
12/22/2025, 5:39 PMMarvin
12/22/2025, 5:40 PMMarvin
12/22/2025, 5:42 PMprefect deploy and sets the image via job variables (no infrastructure blocks in Prefect 3)
Save as `.github/workflows/prefect-3-deploy.yml`:
name: Build and Deploy Prefect 3 to Cloud
on:
push:
branches: [ "main" ]
workflow_dispatch:
permissions:
contents: read
id-token: write
env:
# Set via GitHub Repository Variables (preferred) or replace inline
GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }}
GAR_REGION: ${{ vars.GAR_REGION }} # e.g. us-central1
GAR_REPO: ${{ vars.GAR_REPO }} # e.g. flows
IMAGE_NAME: ${{ vars.IMAGE_NAME }} # e.g. my-flow
WORK_POOL_NAME: ${{ vars.WORK_POOL_NAME }} # e.g. docker-pool or kubernetes-pool
GCS_BUCKET: ${{ vars.GCS_BUCKET }} # optional: bucket to also upload image tar
# Prefect Cloud auth
PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }}
PREFECT_API_URL: ${{ secrets.PREFECT_API_URL }} # Workspace API URL
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Prefect 3
run: |
python -m pip install --upgrade pip
pip install "prefect>=3,<4"
- name: Authenticate to Google Cloud (service account JSON)
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Set up gcloud SDK
uses: google-github-actions/setup-gcloud@v2
with:
project_id: ${{ env.GCP_PROJECT_ID }}
- name: Compute image tags
run: |
echo "IMAGE_TAG=${GITHUB_SHA::12}" >> $GITHUB_ENV
echo "REGISTRY_HOST=${{ env.GAR_REGION }}-docker.pkg.dev" >> $GITHUB_ENV
echo "FULL_IMAGE=${{ env.GAR_REGION }}-docker.pkg.dev/${{ env.GCP_PROJECT_ID }}/${{ env.GAR_REPO }}/${{ env.IMAGE_NAME }}:${GITHUB_SHA::12}" >> $GITHUB_ENV
echo "FULL_IMAGE_LATEST=${{ env.GAR_REGION }}-docker.pkg.dev/${{ env.GCP_PROJECT_ID }}/${{ env.GAR_REPO }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_ENV
- name: Configure Docker auth for Artifact Registry
run: |
gcloud auth configure-docker $REGISTRY_HOST --quiet
- name: Build image
run: |
docker build -t "$FULL_IMAGE" -t "$FULL_IMAGE_LATEST" .
- name: Push image to Artifact Registry
run: |
docker push "$FULL_IMAGE"
docker push "$FULL_IMAGE_LATEST"
# Optional: also upload a compressed image tar to GCS (only if you truly need it)
- name: Optionally upload image tar to GCS
if: env.GCS_BUCKET != ''
run: |
docker save "$FULL_IMAGE" | gzip > image.tar.gz
gsutil cp image.tar.gz gs://$GCS_BUCKET/prefect-images/${{ env.IMAGE_NAME }}-${{ env.IMAGE_TAG }}.tar.gz
- name: Verify Prefect auth
run: prefect version
# Deploy using prefect.yaml in the repo; override the image via job variables
# This works for Docker or Kubernetes work pools — both accept an "image" job variable.
- name: Deploy from prefect.yaml with image override
run: |
# Deploy all defined deployments in prefect.yaml, setting work pool and image
prefect deploy --all \
--pool "${WORK_POOL_NAME}" \
-jv "image=${FULL_IMAGE}"
Notes:
- In Prefect 3.x, you don’t update “infrastructure blocks.” Instead, you set the container image via job variables on deploy (as shown with -jv image=...).
- For Kubernetes pools, you can append more job variables (edit the deploy step), e.g.:
- -jv 'namespace=prefect'
- -jv 'cpu_request=500m' -jv 'memory_request=512Mi'Marvin
12/22/2025, 5:42 PM-jv 'env={"MY_VAR":"value"}'
- If you really need to run from a GCS tar, make sure your workers load that tar before execution; otherwise, prefer Artifact Registry so workers can pull by image tag.
You’ll need:
- GitHub secrets: PREFECT_API_KEY, PREFECT_API_URL, GCP_CREDENTIALS
- GitHub variables: GCP_PROJECT_ID, GAR_REGION, GAR_REPO, IMAGE_NAME, WORK_POOL_NAME (and optionally GCS_BUCKET)
Helpful references:
- Prefect deploy CLI (Prefect 3): prefect deploy docs
- Work pools and job variables: Work pools and job variables
- Google Auth GitHub Action: google-github-actions/auth
- Google Cloud SDK setup: setup-gcloud
Want me to tailor this for your exact pool type (Docker vs Kubernetes) and your prefect.yaml layout? If you paste your pool name and whether you’re on GKE or plain Docker, I’ll tighten the job variables and entrypoints.Florent VanDeMoortele
12/22/2025, 5:44 PMname: Deployment
run-name: Deployment for ${{ github.repository }} from ${{ github.ref_name }} by @${{ github.actor }} on ${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }} environment
on:
release:
types: [ published ]
workflow_dispatch:
inputs:
environment:
description: The environment to deploy to
required: true
type: choice
options:
- dev
- prod
default: dev
jobs:
tests:
uses: ./.github/workflows/unit_tests.yaml
secrets: inherit
deploy:
needs: [tests]
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: 3.14
- name: Load cached Poetry installation
id: cached-poetry
uses: actions/cache@v4
with:
path: ~/.local
key: poetry-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}
- name: Install Poetry
if: steps.cached-poetry.outputs.cache-hit != 'true'
uses: snok/install-poetry@v1
with:
version: 2.2.1
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction
- name: Prefect Auth
uses: PrefectHQ/actions-prefect-auth@v1
with:
prefect-api-key: ${{ secrets.PREFECT_API_KEY }}
prefect-workspace: ${{ secrets.PREFECT_WORKSPACE }}
- name: GCP authentication
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Configure Google Cloud credential helper
run: gcloud auth configure-docker --quiet ${{ secrets.GCP_REGION }}-docker.pkg.dev
- name: Set Iguazu variables
run: |
echo "PROJECT_NAME=$(poetry version | awk '{print $1}')" >> $GITHUB_ENV
echo "PROJECT_VERSION=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV
echo "PREFECT_VERSION=$(poetry show | grep "prefect " | awk '{print $2}')" >> $GITHUB_ENV
echo "PREFECT_ACCOUNT_ID=$(prefect config view | tr -d "\n" | awk -F"PREFECT_API_URL='" '{print $2}' | awk -F"'" '{print $1}' | awk -F"/" '{print $6}')" >> $GITHUB_ENV
echo "PREFECT_WORKSPACE_ID=$(prefect config view | tr -d "\n" | awk -F"PREFECT_API_URL='" '{print $2}' | awk -F"'" '{print $1}' | awk -F"/" '{print $8}')" >> $GITHUB_ENV
- name: Set secrets variables
run: |
echo "DEPLOYMENT_ENV=${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }}" >> $GITHUB_ENV
echo "GCP_PROJECT_ID=${{ secrets.GCP_PROJECT_ID }}" >> $GITHUB_ENV
echo "GCP_REGION=${{ secrets.GCP_REGION }}" >> $GITHUB_ENV
- name: Prefect Deploy Blocks
run: poetry run prefect block register -m iguazu.blocks.papi
- name: Prefect Deploy Iguazu project
run: poetry run prefect --no-prompt deploy --allMarvin
12/22/2025, 5:45 PMMarvin
12/22/2025, 5:46 PMprefect deploy (do not use the removed prefect deployment build).
- Prefer work pools + job variables (e.g., image) instead of infrastructure blocks. If your iguazu.blocks.papi registers non-infrastructure blocks (e.g., creds), keep it. If it registered infra blocks (e.g., DockerContainer), remove/replace with work pool settings in prefect.yaml.
- Ensure your prefect.yaml is updated to Prefect 3 (build/push steps and work_pool.job_variables).
- Use a supported Python version. Python 3.14 is not supported by Prefect right now; use 3.11 or 3.12.
Minimal GitHub Actions update (preserves your flow and relies on prefect.yaml to build/push the image):
```
name: Deployment
run-name: Deployment for ${{ github.repository }} from ${{ github.ref_name }} by @${{ github.actor }} on ${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }} environment
on:
release:
types: [ published ]
workflow_dispatch:
inputs:
environment:
description: The environment to deploy to
required: true
type: choice
options: [ dev, prod ]
default: dev
jobs:
tests:
uses: ./.github/workflows/unit_tests.yaml
secrets: inherit
deploy:
needs: [tests]
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
id: setup-python
uses: actions/setup-python@v5
with:
# Prefect 3 supports 3.11/3.12; avoid 3.14
python-version: "3.11"
- name: Load cached Poetry installation
id: cached-poetry
uses: actions/cache@v4
with:
path: ~/.local
key: poetry-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}
- name: Install Poetry
if: steps.cached-poetry.outputs.cache-hit != 'true'
uses: snok/install-poetry@v1
with:
version: 2.2.1
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction
# IMPORTANT: ensure your pyproject.toml pins Prefect 3 (e.g., prefect = ">=3,<4")
- name: Prefect Auth
uses: PrefectHQ/actions-prefect-auth@v1
with:
prefect-api-key: ${{ secrets.PREFECT_API_KEY }}
prefect-workspace: ${{ secrets.PREFECT_WORKSPACE }}
- name: GCP authentication
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Configure Google Cloud credential helper
# Needed if your prefect.yaml build/push steps publish to Artifact Registry
run: gcloud auth configure-docker --quiet ${{ secrets.GCP_REGION }}-docker.pkg.dev
- name: Set variables (optional; keep if used by your project)
run: |
echo "PROJECT_NAME=$(poetry version | awk '{print $1}')" >> $GITHUB_ENV
echo "PROJECT_VERSION=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV
echo "PREFECT_VERSION=$(poetry show | grep "prefect " | awk '{print $2}')" >> $GITHUB_ENV
# These IDs are generally not needed for deploys; safe to remove if unused
echo "PREFECT_ACCOUNT_ID=$(prefect config view | tr -d "\n" | awk -F"PREFECT_API_URL='" '{print $2}' | awk -F"'" '{print $1}' | awk -F"/" '{print $6}')" >> $GITHUB_ENVMarvin
12/22/2025, 5:46 PMUpdate your prefect.yaml for Prefect 3
Your workflow relies on prefect.yaml to build/push images and define the work pool. In Prefect 3, use steps like these:
# prefect.yaml (Prefect 3)
name: iguazu
prefect-version: ">=3.0.0"
# Build image
build:
- prefect_docker.deployments.steps.build_docker_image:
id: build-image
image_name: "{{ $IMAGE_NAME | default('us-central1-docker.pkg.dev/your-proj/your-repo/iguazu') }}"
tag: "{{ $GIT_SHA | default('latest') }}"
dockerfile: auto
# Push to Artifact Registry
push:
- prefect_docker.deployments.steps.push_docker_image:
image_name: "{{ build-image.image_name }}"
tag: "{{ build-image.tag }}"
# Optional: set working directory inside the image at runtime
pull:
- prefect.deployments.steps.set_working_directory:
directory: /opt/prefect
deployments:
- name: iguazu
entrypoint: path/to/your_flow.py:your_flow
work_pool:
name: docker-pool # or your kubernetes pool name
job_variables:
image: "{{ build-image.image }}" # <- critical in Prefect 3
# For k8s pools you can add more:
# namespace: "prefect"
# cpu_request: "500m"
# memory_request: "512Mi"
If you want to override the image from the Action instead of relying on prefect.yaml, change the deploy step to:
poetry run prefect --no-prompt deploy --all \
--pool "your-pool-name" \
-jv "image=REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:${GITHUB_SHA::12}"
```
Common pitfalls when moving to Prefect 3
- Python 3.14: not supported yet. Use 3.11/3.12.
- Infrastructure blocks: don’t use them; replace with work pool job variables (e.g., image).
- prefect.yaml: ensure you’re using Prefect 3 steps (prefect_docker.*) and set work_pool.job_variables.image.
- Don’t use prefect deployment build or Deployment.build_from_flow() (removed).
If you share your prefect.yaml (redacting secrets) and your work pool type (Docker vs Kubernetes on GKE), I can tailor the job variables and the exact deploy command for your setup.
Handy docs:
- Deploying from a project (Prefect 3): prefect deploy docs
- Work pools and job variables: Work pools and job variables
- Prefect Docker steps: Docker deploy stepsFlorent VanDeMoortele
12/22/2025, 5:52 PMMarvin
12/22/2025, 5:52 PMMarvin
12/22/2025, 5:54 PMprefect deploy (Prefect 3 CLI)
- Ensure your project is pinned to Prefect 3 in pyproject (e.g., prefect = ">=3,<4")
- Use work pools + job variables in prefect.yaml (not infrastructure blocks). Keep your custom blocks registration only if they’re not infra blocks.
Drop-in updated workflow:
```
name: Deployment
run-name: Deployment for ${{ github.repository }} from ${{ github.ref_name }} by @${{ github.actor }} on ${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }} environment
on:
release:
types: [ published ]
workflow_dispatch:
inputs:
environment:
description: The environment to deploy to
required: true
type: choice
options: [ dev, prod ]
default: dev
jobs:
tests:
uses: ./.github/workflows/unit_tests.yaml
secrets: inherit
deploy:
needs: [tests]
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Load cached Poetry installation
id: cached-poetry
uses: actions/cache@v4
with:
path: ~/.local
key: poetry-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}
- name: Install Poetry
if: steps.cached-poetry.outputs.cache-hit != 'true'
uses: snok/install-poetry@v1
with:
version: 2.2.1
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction
# Ensure pyproject pins Prefect 3: prefect = ">=3,<4"
- name: Prefect Auth
uses: PrefectHQ/actions-prefect-auth@v1
with:
prefect-api-key: ${{ secrets.PREFECT_API_KEY }}
prefect-workspace: ${{ secrets.PREFECT_WORKSPACE }}
- name: GCP authentication
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Configure Google Cloud credential helper
run: gcloud auth configure-docker --quiet ${{ secrets.GCP_REGION }}-docker.pkg.dev
- name: Set variables (optional)
run: |
echo "PROJECT_NAME=$(poetry version | awk '{print $1}')" >> $GITHUB_ENV
echo "PROJECT_VERSION=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV
echo "PREFECT_VERSION=$(poetry show | grep "prefect " | awk '{print $2}')" >> $GITHUB_ENV
echo "DEPLOYMENT_ENV=${{ github.event.inputs.environment == 'prod' && 'prod' || 'dev' }}" >> $GITHUB_ENV
echo "GCP_PROJECT_ID=${{ secrets.GCP_PROJECT_ID }}" >> $GITHUB_ENV
echo "GCP_REGION=${{ secrets.GCP_REGION }}" >> $GITHUB_ENV
# The following IDs are usually not needed for deploys; keep only if used elsewhere:
echo "PREFECT_ACCOUNT_ID=$(prefect config view | tr -d "\n" | awk -F"PREFECT_API_URL='" '{print $2}' | awk -F"'" '{print $1}' | awk -F"/" '{print $6}')" >> $GITHUB_ENV
echo "PREFECT_WORKSPACE_ID=$(prefect config view | tr -d "\n" | awk -F"PREFECT_API_URL='" '{print $2}' | awk -F"'" '{print $1}' | awk -F"/" '{print $8}')" >> $GITHUB_ENVMarvin
12/22/2025, 5:54 PMDouble-check your prefect.yaml (Prefect 3 format) includes:
- Docker build/push steps (if you build/push in deploy)
- A work pool, and job variables set for the image used by your workers, e.g.:
deployments:
- name: iguazu
entrypoint: path/to/your_flow.py:your_flow
work_pool:
name: your-docker-or-k8s-pool
job_variables:
image: "REGION-docker.pkg.dev/PROJECT/REPO/IMAGE:tag"
If you prefer to override the image from the Action instead of prefect.yaml, you can:
poetry run prefect --no-prompt deploy --all \
--pool "your-pool-name" \
-jv "image=${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/REPO/IMAGE:${GITHUB_SHA::12}"
```
Helpful references:
- Deploy from a project (Prefect 3): prefect deploy docs
- Work pools and job variables: Work pools and job variables
- Prefect Auth action: actions-prefect-auth
- Google Auth action: google-github-actions/auth
If you share your prefect.yaml and pool type (Docker vs GKE), I can validate the job variables and build/push steps to ensure everything runs smoothly on 3.14.