Thanks—that snippet explains the behavior. You’re building an image locally during deploy, but push=False means the image never makes it to the Docker worker’s host. When the worker tries to start the flow, it can’t find the image locally, so it attempts to pull it from a registry. Since your image name is just flow_name:environment (e.g., data_unload_local:local) and not in any registry, the pull fails with “access denied.” On the next run, you sometimes get lucky (e.g., same daemon, image now present), so it works. Computers are nothing if not consistent—except when they aren’t.
You’ve got two clean ways to stop the crash—pick one:
Option A: Use a registry (recommended if the worker isn’t on the same machine)
- Tag for a registry and push the image during deploy:
- Example:
image=DockerImage(
name="
ghcr.io/your-org/data_unload", # or ECR/GCR/ACR/Docker Hub namespace
tag=environment,
dockerfile="data_flow.Dockerfile",
rm=True
)
.deploy(..., push=True, ...)
- Ensure the worker can pull:
- docker login on the worker host (or configure credentials in whatever way you normally do)
- Use the fully qualified image name above in your deployment
- Docs: Docker deployments in Prefect 3
-
https://docs-3.prefect.io/v3/how-to-guides/deployment_infra/docker
Option B: Keep the image local-only (no registry)
- Make sure the worker uses the same Docker daemon where the image exists.
- If your worker runs elsewhere, preload the image onto the worker:
- Build on the worker host, or
- docker save data_unload_local:local | ssh worker-host docker load
- If your Docker work pool supports a pull policy, set it to IfNotPresent or Never so it won’t try to pull when the image exists locally.
- Inspect your pool to see available variables:
- prefect work-pool inspect docker-worker-pool
- Look for keys like pull_policy or auto_pull in the base job template. If present, set via job_variables or pool template. If not present, preloading the image is the reliable route.
Why your current config fails
- Your call builds locally (default build=True) but push=False, so the worker’s host never gets the image.
- The worker tries to pull data_unload_local:local from a registry; that’s not a published repo, so it’s denied.
Minimal changes for each path
- Registry path:
- Change name to include a registry namespace and set push=True:
image=DockerImage(
name="
ghcr.io/your-org/data_unload",
tag=environment,
dockerfile="data_flow.Dockerfile",
rm=True
)
- .deploy(..., push=True, ...)
- Local-only path:
- Keep your code as-is but ensure the worker has the image locally before the run:
docker build -t data_unload_local:local -f data_flow.Dockerfile .
# on the worker host
docker load < image.tar # if you transfer it
- Optionally set the pool’s pull policy (if available) to avoid pulling.
If you tell me where your Docker worker runs relative to where you build this image (same host or remote), and whether you prefer using a registry or staying local-only, I’ll give you the exact config and commands to make it boringly reliable.