Carlos Alberto da Costa Filho
04/06/2026, 11:04 PMStorage is not configured for work pool 'work-gpu'. Please configure storage for the work pool by running `prefect work-pool storage configure`.
Can you explain why this is needed now (and wasn't needed before). Also, what does this storage mean exactly? The docs are not very descriptive:
https://docs.prefect.io/v3/api-ref/cli/work-pool#prefect-work-pool-storageMarvin
04/06/2026, 11:04 PMMarvin
04/06/2026, 11:08 PMprefect work-pool storage configure s3 <POOL_NAME> ...
prefect work-pool storage configure gcs <POOL_NAME> ...
prefect work-pool storage configure azure-blob-storage <POOL_NAME> ...
- CLI reference: prefect work-pool storage
Why it’s needed now (and wasn’t before)
- Before (e.g., with deployments and/or custom images), your flow’s source was either:
- Pulled from a repo via deployment storage (flow.from_source(...).deploy(...)), or
- Already baked into the container image you ran.
- With the experimental @kubernetes path, Prefect serializes your flow + local imports (+ optional include_files) into a bundle and:
- Uploads it to the pool’s configured storage
- Starts a Kubernetes job whose first step is to download and execute that bundle
- Because the worker pod needs a consistent place to fetch that bundle, the pool must have storage configured.
What happens under the hood with @kubernetes
- When you call the decorated flow for remote execution via a work pool:
1) Prefect creates a bundle (your flow function + local modules + optional include_files).
2) Uploads that bundle to the pool’s storage (S3/GCS/Azure).
3) The Kubernetes job uses a Prefect image, downloads the bundle from that storage, installs any needed deps, and runs the flow.
- No volume mounts of your source are required; your code is shipped as the bundle.
- More background: Submit flows directly to dynamic infrastructure
When it’s required
- Required when you run via a work pool (e.g., passing work_pool="work-gpu" on the decorator or using .submit_to_work_pool()).
- Not required for:
- Local/in-process execution (calling the flow like a normal function)
- Classic deployments that use flow.from_source(...).deploy(...) (code is sourced by the deployment), or
- Images that already contain your code (typical deployment pattern)
How to configure
- S3 example:
# Create or reuse an AWS credentials block first (name shown here as my-aws-creds)
prefect work-pool storage configure s3 work-gpu \
--bucket my-prefect-bundles \
--aws-credentials-block-name my-aws-creds
- GCS example:
prefect work-pool storage configure gcs work-gpu \
--bucket my-prefect-bundles \
--service-account-info-block-name my-gcp-creds
- Inspect what’s set:
prefect work-pool storage inspect work-gpu
If you share which cloud you’re using (S3, GCS, Azure) and how you were running flows before (deployments vs. custom images), I can give you the exact command(s) to run and help you migrate smoothly.Carlos Alberto da Costa Filho
04/06/2026, 11:09 PM@flow(result_storage=S3Block) is NOT enough rightMarvin
04/06/2026, 11:09 PMMarvin
04/06/2026, 11:10 PM@flow(result_storage=S3Block) is not enough.
- Flow result_storage controls where your flow’s outputs are persisted.
- Work pool storage controls where the code bundle (your flow + local deps + optional files) is uploaded so the Kubernetes worker/pod can download and execute it.
With the experimental @kubernetes path, when you run via a work pool (e.g., work_pool="work-gpu" or .submit_to_work_pool()), Prefect must upload a bundle to pool storage and then the pod fetches it. Even if you set result_storage on the flow, the worker still needs pool storage for the bundle itself.
Notes:
- The pool storage config also wires up a default result storage for runs from that pool. Your @flow(result_storage=...) can override where results land, but it does not replace the bundle upload/download steps.
- Pool storage is not required if you run locally/in-process or use other patterns (e.g., classic deployments with flow.from_source(...).deploy(...), or direct .submit() to a cluster you can access). It is required for the experimental work-pool-backed path.
CLI reference and docs:
- Work pool storage commands: prefect work-pool storage
- Background on direct-to-infrastructure execution: Submit flows directly to dynamic infrastructureCarlos Alberto da Costa Filho
04/07/2026, 12:23 AMMarvin
04/07/2026, 12:23 AMMarvin
04/07/2026, 12:23 AM@flow(result_storage=...) does in 3.x.
What’s new in 3.x with the experimental decorators:
- Pool-level “work pool storage” is specifically for the bundle-based execution path used by decorators like @kubernetes. Prefect packages your flow + local deps/files into a bundle, uploads it to the pool’s configured storage, and the worker/pod downloads and executes it.
- This pool-scoped storage requirement didn’t exist in 2.x because code delivery was handled per-deployment (storage blocks) or via images, not via a shared bundle store at the pool level.
So in 2.x you would have:
- Used deployment storage blocks or images to get code onto the remote runner
- No need to configure a pool-level storage target
In 3.x (experimental @kubernetes path), you now configure pool storage once so any flow submitted to that pool can upload its bundle and be executed by the worker.Carlos Alberto da Costa Filho
04/07/2026, 12:24 AM@kubernetes ?Marvin
04/07/2026, 12:24 AMMarvin
04/07/2026, 12:26 AMprefect.utilities.dockerutils.get_prefect_image_name().
What that resolves to:
- Production installs: prefecthq/prefect:<your_prefect_version>-python<your_python_minor>
- Example: prefecthq/prefect:3.1.2-python3.11
- Dev/nightly installs: prefecthq/prefect-dev:sha-<7char_git_sha>-python<minor>
- Example: prefecthq/prefect-dev:sha-ab12cd3-python3.11
How to check on your machine:
from prefect.utilities.dockerutils import get_prefect_image_name
print(get_prefect_image_name())
How to override:
- Directly on the decorator:
from prefect import flow
from prefect_kubernetes.experimental import kubernetes
@kubernetes(work_pool="work-gpu", image="my-registry/my-prefect-cuda:latest")
@flow
def my_flow():
...
- Or set a base job template on the work pool with your desired image.
Note: For GPU workloads, you’ll typically want to specify your own CUDA-enabled image; the default Prefect images are CPU-only.Carlos Alberto da Costa Filho
04/07/2026, 12:28 AMMarvin
04/07/2026, 12:28 AMMarvin
04/07/2026, 12:30 AMfrom prefect import flow
from prefect_kubernetes.experimental import kubernetes
@kubernetes(
work_pool="my-k8s-pool",
image="my-registry/prefect-cuda:12.2-runtime" # your custom image here
)
@flow
def my_flow():
import platform
return f"Running on: {platform.platform()}"
# Local submission to a work pool (requires worker running)
my_flow.submit_to_work_pool()
You can combine image with other job variables (namespace, resources, env, etc.) the same way:
@kubernetes(
work_pool="my-k8s-pool",
image="registry/my-image:latest",
namespace="ml",
cpu="2",
memory="8Gi",
env={"EXTRA_FLAG": "1"},
)
@flow
def train():
...
If you’d like, tell me your registry and resource needs (e.g., GPU, CUDA version), and I’ll tailor an example manifest and image suggestion.Carlos Alberto da Costa Filho
04/07/2026, 12:33 AMMarvin
04/07/2026, 12:33 AMMarvin
04/07/2026, 12:34 AMprefect work-pool create my-k8s-pool --type kubernetes
prefect work-pool storage configure s3 my-k8s-pool --bucket my-bundles --aws-credentials-block-name my-aws-creds
- Optional: Start a worker yourself (useful for .submit_to_work_pool):
prefect worker start --pool my-k8s-pool
Why storage is still required locally
- The experimental path always uses bundles for remote execution. Even with an ephemeral server, the pod must download your code bundle from cloud storage; that’s what the pool storage config enables.
If you want, tell me which cloud you’re using (S3/GCS/Azure), and I’ll give you exact commands to set up the pool storage for your local ephemeral setup.Carlos Alberto da Costa Filho
04/07/2026, 12:35 AMMarvin
04/07/2026, 12:35 AMMarvin
04/07/2026, 12:35 AMCarlos Alberto da Costa Filho
04/07/2026, 12:37 AMMarvin
04/07/2026, 12:37 AMMarvin
04/07/2026, 12:39 AMprefect work-pool storage configure s3 ... automates.
Outline of an automated setup (Python client)
from prefect.client.orchestration import get_client
from prefect.client.schemas.actions import WorkPoolUpdate, BlockDocumentCreate, BlockDocumentUpdate
from prefect.client.schemas.objects import WorkPoolStorageConfiguration
from prefect.exceptions import ObjectNotFound
POOL = "work-gpu"
BUCKET = "my-prefect-bundles"
AWS_CREDS_BLOCK = "my-aws-creds"
RESULT_BLOCK_NAME = f"default-{POOL}-result-storage"
async def configure_pool_storage():
async with get_client() as client:
# 1) Ensure result storage block exists/updated
try:
result_block = await client.read_block_document_by_name(
name=RESULT_BLOCK_NAME, block_type_slug="s3-bucket"
)
await client.update_block_document(
block_document_id=result_block.id,
block_document=BlockDocumentUpdate(
data={
"bucket_name": BUCKET,
"credentials": {"$ref": {"block_document_name": AWS_CREDS_BLOCK}},
}
),
)
except ObjectNotFound:
block_type = await client.read_block_type_by_slug("s3-bucket")
schema = await client.get_most_recent_block_schema_for_block_type(block_type.id)
result_block = await client.create_block_document(
block_document=BlockDocumentCreate(
name=RESULT_BLOCK_NAME,
block_type_id=block_type.id,
block_schema_id=schema.id,
data={
"bucket_name": BUCKET,
"credentials": {"$ref": {"block_document_name": AWS_CREDS_BLOCK}},
},
)
)
# 2) Update the work pool with bundle upload/execute steps
await client.update_work_pool(
work_pool_name=POOL,
work_pool=WorkPoolUpdate(
storage_configuration=WorkPoolStorageConfiguration(
bundle_upload_step={
"prefect_aws.experimental.bundles.upload": {
"requires": "prefect-aws",
"bucket": BUCKET,
"aws_credentials_block_name": AWS_CREDS_BLOCK,
}
},
bundle_execution_step={
"prefect_aws.experimental.bundles.execute": {
"requires": "prefect-aws",
"bucket": BUCKET,
"aws_credentials_block_name": AWS_CREDS_BLOCK,
}
},
default_result_storage_block_id=result_block.id,
)
),
)
If you’re committed to Terraform-only workflows:
- You can still use Terraform to provision AWS resources (bucket, IAM) and possibly Prefect blocks that the API can reference, but you’ll need a small post-apply step (e.g., a script) to call Prefect’s API to wire the work pool storage config.
- If you’d like first-class Terraform support for work pool storage, consider opening/👍 an issue at https://github.com/PrefectHQ/terraform-provider-prefect/issues and we can track it.
If you tell me your desired bucket/credential block naming scheme, I can hand you a complete, ready-to-run script you can invoke from Terraform as a null_resource/local-exec step.