Kurt Sys (Vintecc)
12/17/2025, 4:38 PMReported flow run 'd3a50672-1f46-40f3-b0ea-af61b13f51ba' as crashed: Flow run could not be submitted to infrastructure:
InfrastructureError('Unable to create Kubernetes job: Forbidden: jobs.batch is forbidden: User "system:serviceaccount:prefect:prefect-worker" cannot create resource "jobs" in API group "batch" in the namespace "default"')
there seem to be issues with the namespaces, but I don't understand where
my deployment script:
import os
from prefect.deployments import deploy
from prefect.deployments.runner import RunnerDeployment
deployment = RunnerDeployment(
name="fraio-trigger",
flow_name="trigger-flow",
entrypoint="fraio_orchestrator.fraio_trigger_flow.trigger_flow",
work_pool_name="fraio-pool",
job_variables={
"image": "harbor.mgmt.vintecc.cloud/vintecc-ags-hbm/fraio_algo:local",
"env": {
"PREFECT_API_URL": "<http://prefect-server.prefect:4200/api>",
"PREFECT_DB_PASSWORD": os.environ["PREFECT_DB_PASSWORD"],
"PREFECT_DB_USER": os.environ["PREFECT_DB_USER"],
"PREFECT_DB_NAME": os.environ["PREFECT_DB_NAME"],
"PREFECT_DB_HOST": os.environ["PREFECT_DB_HOST"],
"PREFECT_DB_PORT": os.environ["PREFECT_DB_PORT"],
},
},
version="local",
tags=[],
description=None,
)
deploy(
deployment,
work_pool_name="fraio-pool",
image="harbor.mgmt.vintecc.cloud/vintecc-ags-hbm/fraio_algo:local",
build=False,
push=False,
print_next_steps_message=True,
ignore_warnings=False,
)
my prefect worker config:
nameOverride: "prefect-worker"
fullnameOverride: "prefect-worker"
namespaceOverride: "prefect"
worker:
apiConfig: selfHostedServer
replicaCount: 2
config:
type: "kubernetes"
workPool: "fraio-pool"
jobNamespace: "prefect"
selfHostedServerApiConfig:
apiUrl: "<http://prefect-server.prefect:4200/api>"
what am I missing here and how to solve it?Marvin
12/17/2025, 4:39 PMMarvin
12/17/2025, 4:43 PMsystem:serviceaccount:prefect:prefect-worker (in the prefect namespace).
- The worker is trying to create Jobs in the default namespace. By default, that SA only has permissions in its own namespace (prefect), so the API denies the request.
Where “default” is coming from
- In Prefect 3.x with the Kubernetes worker, the Job namespace defaults to default unless you set it in one of these places (highest precedence first):
1) deployment job_variables["namespace"]
2) your Kubernetes work pool’s base job template (the “namespace” variable)
3) default (“default”)
Note: The Helm value worker.config.jobNamespace does NOT automatically override the work pool/deployment job namespace. It primarily influences what the worker observes and defaults, but the submitted Job’s namespace is read from the work pool/deployment template.
Two solid ways to fix it
Option A — Run jobs in the prefect namespace (recommended):
- Set the namespace on your deployment (quickest):
deployment = RunnerDeployment(
name="fraio-trigger",
flow_name="trigger-flow",
entrypoint="fraio_orchestrator.fraio_trigger_flow.trigger_flow",
work_pool_name="fraio-pool",
job_variables={
"image": "harbor.mgmt.vintecc.cloud/vintecc-ags-hbm/fraio_algo:local",
"namespace": "prefect",
"service_account_name": "prefect-worker",
"env": {
"PREFECT_API_URL": "<http://prefect-server.prefect:4200/api>",
"PREFECT_DB_PASSWORD": os.environ["PREFECT_DB_PASSWORD"],
"PREFECT_DB_USER": os.environ["PREFECT_DB_USER"],
"PREFECT_DB_NAME": os.environ["PREFECT_DB_NAME"],
"PREFECT_DB_HOST": os.environ["PREFECT_DB_HOST"],
"PREFECT_DB_PORT": os.environ["PREFECT_DB_PORT"],
},
},
version="local",
tags=[],
description=None,
)
- Or set it once at the work pool level so all deployments inherit it:
- In the UI: Work Pools → open fraio-pool (must be type “kubernetes”) → Configure → set Namespace = prefect.
- Or via CLI with a base job template (you can update an existing pool):
1) Inspect current pool:
prefect work-pool inspect fraio-pool
2) Create a small JSON file (e.g., k8s-template.json) with a default namespace:
{
"variables": {
"namespace": {
"default": "prefect"
}
}
}
3) Apply it (create can also update when used with overwrite):
prefect work-pool create "fraio-pool" --type kubernetes --base-job-template ./k8s-template.json --overwrite
Option B — Keep jobs in default and grant RBAC there:
- Create a Role/RoleBinding in the default namespace so prefect:prefect-worker can create Jobs:
apiVersion: <http://rbac.authorization.k8s.io/v1|rbac.authorization.k8s.io/v1>
kind: Role
metadata:
name: prefect-worker
namespace: default
rules:
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get","list","watch","create","update","patch","delete"]
- apiGroups: [""]
resources: ["pods","pods/log","events"]
verbs: ["get","list","watch"]
---
apiVersion: <http://rbac.authorization.k8s.io/v1|rbac.authorization.k8s.io/v1>
kind: RoleBinding
metadata:
name: prefect-worker
namespace: default
roleRef:
apiGroup: <http://rbac.authorization.k8s.io|rbac.authorization.k8s.io>
kind: Role
name: prefect-worker
subjects:
- kind: ServiceAccount
name: prefect-worker
namespace: prefect
Sanity checks
- Confirm your work pool type and current template:
prefect work-pool inspect fraio-pool
- Verify RBAC from the worker SA:
kubectl auth can-i create jobs --as=system:serviceaccount:prefect:prefect-worker -n default
kubectl auth can-i create jobs --as=system:serviceaccount:prefect:prefect-worker -n prefect
A couple of small notes
- Ensure fraio-pool is a Kubernetes work pool. If it’s not, create it:
prefect work-pool create "fraio-pool" --type kubernetesMarvin
12/17/2025, 4:43 PMprefect work-pool inspect fraio-pool (especially the “namespace” variable)
- Whether you’d prefer to run jobs in prefect or default
…I can tailor the exact steps/yaml for your setup.