Tomer Gafsou
12/02/2025, 2:53 PMMarvin
12/02/2025, 2:53 PMMarvin
12/02/2025, 2:58 PMprefect deploy loads/imports your flow at deploy time to derive metadata (e.g., parameter schema) and validate the entrypoint. That import happens on the machine running the CLI. So even if you specify a Docker image in prefect.yaml, the CI node still tries to import your flow and its dependencies locally — which explains the missing dependency traceback.
Ways to fix it:
Option 1 — Run the deploy inside your prebuilt image
Since your image already has the code and dependencies, run the CLI from that image in CI. For example:
docker run --rm \
-e PREFECT_API_URL=$PREFECT_API_URL \
-e PREFECT_API_KEY=$PREFECT_API_KEY \
-w /app \
my-registry/my-image:tag \
prefect deploy --prefect-file /app/prefect.yaml --name some_name
Notes:
- Ensure prefect.yaml is in the image at /app/prefect.yaml (or mount it: -v $PWD/prefect.yaml:/app/prefect.yaml).
- Set -w to the project root so relative paths in prefect.yaml resolve.
- This keeps the deployment creation in the same environment your flow uses, avoiding missing deps on the CI host.
Option 2 — Install your project deps on the CI node before deploy
If you prefer to keep using the CI host, install the dependencies required to import your flow before running prefect deploy. This doesn’t need Docker; it just needs enough packages so the module import succeeds.
Option 3 — Use a small deploy script inside the image (Python API)
If you don’t want to rely on prefect.yaml, you can run a short script inside the image that imports the flow and calls .deploy() while reusing your prebuilt image (no build/push at deploy time):
python - << 'EOF'
import asyncio
from prefect import flow
# Import your flow from the code inside the image
from my_package.flows import my_flow # adjust this import
async def main():
await my_flow.deploy(
name="some_name",
work_pool_name="your-work-pool",
image="my-registry/my-image:tag",
build=False, # prebuilt
push=False, # already pushed
job_variables={"image": "my-registry/my-image:tag"},
)
asyncio.run(main())
EOF
Then run it in CI via:
docker run --rm \
-e PREFECT_API_URL=$PREFECT_API_URL \
-e PREFECT_API_KEY=$PREFECT_API_KEY \
my-registry/my-image:tag \
bash -lc '<the heredoc above>'
Why the CLI behaves this way
- The CLI validates the entrypoint and inspects the flow by importing it. If your module imports heavy libraries at module import time (e.g., pandas) and those aren’t installed on CI, the import fails.
- Specifying a Docker image in prefect.yaml tells workers what to run at execution time, but it does not change how deployment creation imports the flow.
If you can share:
- Your Prefect version (assuming 3.x),
- The top of the traceback,
- The deployments: section of your prefect.yaml (especially entrypoint, work_pool, and any pull steps),
…I can confirm exactly where the import is failing and tailor the command/YAML accordingly.