Ferchau Gupta
07/20/2026, 1:16 PMMarvin
07/20/2026, 1:16 PMMarvin
07/20/2026, 1:18 PMprocess run starts by spawning a local subprocess on an already-running machine. A Kubernetes run has extra steps before your flow code can begin:
1. Prefect creates the K8s Job
2. Kubernetes creates the Pod
3. The scheduler picks a node
4. The node may need to pull the image
5. The container starts Python/Prefect
6. Then your flow code begins
So ~6s for process vs ~15s for k8s job is very plausible in Prefect 2.20.
Why process is faster
- Local subprocess launch is cheap
- No pod scheduling
- No container startup
- No image pull
- No K8s API round-trip / job lifecycle overhead
Why k8s is slower
Even for the same flow, K8s adds fixed startup overhead from:
- Kubernetes API job creation
- pod scheduling delay
- container runtime startup
- possible image pull
- cluster/node/network latency
In practice, the biggest contributors are usually:
- image pull time if the image is not already cached on the node
- scheduler wait time if the cluster is busy or autoscaling
- container startup/import time if the image is heavy
A rough mental model:
- `process`: “start Python here”
- `k8s`: “ask another system to create a container somewhere, wait for it to be ready, then start Python”
What the Prefect UI timing usually includes
If you’re comparing from flow-run creation to first flow logs, the K8s path includes infrastructure bring-up time, while process usually reaches “user code started” much sooner.
Most likely reason for your 9-second gap
For a jump from ~6s to ~15s, the usual culprits are:
- image not cached
- pod scheduling latency
- cluster autoscaler / limited node capacity
- larger/heavier image startup
How to verify
Check the Kubernetes pod events for the run:
bash
kubectl describe pod <pod-name> -n <namespace>
Look for timestamps around:
- Scheduled
- Pulling image
- Pulled
- Created
- Started
That will tell you exactly where the extra ~9 seconds went.
How to reduce K8s startup time
- Use a smaller image
- Set image pull policy to IfNotPresent
- Keep images cached on nodes
- Avoid scale-to-zero / cold autoscaling if low latency matters
- Right-size CPU/memory requests so scheduling is easier
- Keep cluster/API latency low
If you want, send the two screenshots and I can help map the exact log lines to the startup phases and estimate where the extra delay is happening.Ferchau Gupta
07/20/2026, 1:27 PMMarvin
07/20/2026, 1:27 PMMarvin
07/20/2026, 1:27 PMbash
kubectl describe pod <pod-name> -n <namespace>
The most useful sections are:
- Events
- Status
- Containers
- the timestamps for:
- Scheduled
- Pulling
- Pulled
- Created
- Started
If easier, just paste the Events: block and I’ll help pinpoint where the extra startup time is coming from.Ferchau Gupta
07/20/2026, 1:28 PMMarvin
07/20/2026, 1:28 PMMarvin
07/20/2026, 1:28 PMkubectl describe pod ...
Best thing to paste is:
text
Events:
...
If you want, you can redact names/IDs. The key lines I need are anything mentioning:
- Scheduled
- Pulling
- Pulled
- Created
- Started
- FailedScheduling or autoscaler eventsFerchau Gupta
07/20/2026, 1:29 PMEvents:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 96s default-scheduler Successfully assigned
Normal Pulling 96s kubelet spec.containers{prefect-job}: Pulling image "xxx.xx.io/xxx"
Normal Pulled 96s kubelet spec.containers{prefect-job}: Successfully pulled image "xxx.xx.io/xxx" in 257ms (257ms including waiting). Image size: 290572417 bytes.
Normal Created 96s kubelet spec.containers{prefect-job}: Container created
Normal Started 96s kubelet spec.containers{prefect-job}: Container startedMarvin
07/20/2026, 1:29 PMMarvin
07/20/2026, 1:29 PMEvents: block, Kubernetes itself is probably not the main source of the extra 9 seconds.
What your pod events show:
- Scheduled
- Pulling
- Pulled
- Created
- Started
all happened essentially immediately, and the image pull was only 257ms.
So this rules out the common K8s bottlenecks:
- not image pull
- not pod scheduling delay
- not node autoscaling
- not slow container creation
What that means
Your extra startup time is likely happening before the pod is created or after the container starts but before your flow code logs appear.
So the delay is probably in one of these two places:
1. Before K8s Job/Pod creation
Possible sources:
- Prefect agent polling interval
- time between flow entering a scheduled/pending state and the agent picking it up
- Kubernetes Job creation round-trip from the Prefect agent
- any deployment/infrastructure preparation Prefect does before submitting the job
2. After container start, before flow code actually runs
Possible sources:
- Python interpreter startup in the container
- Prefect package import/init
- pulling flow code from remote storage
- installing/loading dependencies at runtime
- heavy module imports in your flow file
- startup scripts / entrypoint overhead
Your pod events suggest the actual K8s infrastructure startup is very fast once the pod exists.
Important implication
The process infrastructure likely starts user code faster because it skips:
- container boot
- separate runtime environment init
- potentially code retrieval inside the container
But in your case, the delta is not mostly due to image pull or scheduling.
How to isolate the gap exactly
Compare these timestamps in Prefect UI/logs:
1. Flow run submitted / picked up by agent
2. Kubernetes job created
3. Pod started ← from your describe output
4. First Prefect engine log inside container
5. First user log from your flow
If the big gap is:
- before step 2: Prefect-side orchestration / polling delay
- between 3 and 4: container command / Python / Prefect startup
- between 4 and 5: importing your flow code or loading from storage
Most likely explanation from the data you shared
Given Scheduled through Started is basically instantaneous, I’d suspect:
- Prefect polling / submission overhead, or
- containerized Python/Prefect/flow import startup overhead
Next thing to check
Please share:
1. the Prefect worker/agent logs around that run
2. the first few logs from inside the flow run
3. the timestamps in UI for:
- run created
- submitted to infrastructure
- first running log
That will let us identify whether the missing ~9 seconds is:
- Prefect before pod creation, or
- inside the container after start
If you want, I can also give you a small “timestamp checklist” to collect from both the process run and the k8s run so we can compare them side by side.Ferchau Gupta
07/20/2026, 2:23 PMMarvin
07/20/2026, 2:23 PMMarvin
07/20/2026, 2:23 PMkubectl describe pod output says:
- scheduling was immediate
- image pull was 257ms
- container started immediately
So if the UI still shows those steps as “taking the longest,” that usually means one of these:
1. Prefect logs are coarse-grained markers
The UI often shows high-level infrastructure messages like:
- creating job
- job created
- pod pending
- running
Those are state/log checkpoints, not a precise trace of every K8s sub-step. The elapsed time between them can include waiting that happened outside the literal action named in the log line.
So “Kubernetes pod is pending” may just mean:
- Prefect emitted that status
- then it waited until it observed the pod/container transition
- the delay may include observer polling / state reconciliation
not necessarily “the scheduler was struggling the whole time.”
2. There can be delay between actual K8s state change and when Prefect observes it
In Prefect 2.x, Kubernetes infrastructure monitoring is not a nanosecond-accurate reflection of pod lifecycle. There can be lag from:
- agent polling
- Kubernetes watch/poll cycles
- Prefect state update timing
- log/event propagation back to the API/UI
So the pod may have started quickly, but Prefect may only reflect that a few seconds later.
3. The time may actually be after container start
Even after the pod is Started, your container still has to:
- launch Python
- import Prefect
- load your flow code
- connect back to Prefect API
- begin emitting logs
That time won’t show up in kubectl describe events, but it will contribute to what you perceive as startup delay in the UI.
So the picture is:
- Kubernetes says pod startup was fast
- Prefect UI says infrastructure phase looked long
- therefore the missing time is probably in Prefect observation/reporting or container startup after pod start
Most likely interpretation
For your case, the extra delay is probably a combination of:
- normal Prefect 2.x K8s orchestration/observation lag
- containerized Prefect runtime startup
- flow import / code loading time
and not actual scheduler/image-pull slowness.
Why process still wins
A process run is simpler:
- local machine already running
- no job object
- no pod lifecycle
- no container entrypoint
- often less runtime initialization
So even if Kubernetes is behaving well, it still has more moving parts and more opportunities for a few extra seconds of control-plane/runtime overhead.
Practical conclusion
Based on the pod events you shared, I would not blame:
- image size
- image pull
- scheduler capacity
- autoscaling
I would instead look at:
- Prefect agent polling / infrastructure submission timing
- delay between pod started and first container log
- heavy imports / startup logic in the container
- remote code fetch/storage setup, if used
Best next check
Run:
bash
kubectl logs <pod-name> -n <namespace> --timestamps
Then compare:
- pod Started time
- first container log time
- first Prefect engine log time
- first user flow log time
If you share those timestamps, I can tell you exactly where the missing seconds are.
If you'd like, I can also help you build a side-by-side timing table for:
- process run
- kubernetes run
so you can pinpoint the exact source of the 6s vs 15s difference.Nate
07/20/2026, 3:43 PM