LIVE

MLflow vs Kubeflow: Pipeline Latency and CPU Overhead

A Kubeflow pipeline can spend more time starting containers than running the machine learning code inside them. With Kubeflow Pipelines v2, each component runs as a separate Kubernetes pod through Argo Workflows.

UpdatedAugust 03, 2026
Read time16 min read
MLflow vs Kubeflow: Pipeline Latency and CPU Overhead

For a cold node, that startup path commonly adds 30–60 seconds per step. On warm nodes, the overhead can fall below 20 seconds, but it does not disappear.

That difference is easy to miss in a small experiment. A single preprocessing task may finish in seconds, while the surrounding platform spends longer creating the pod, pulling the image, scheduling resources, and initializing the container. Repeat that pattern across 20 sequential lightweight steps and the pipeline can accumulate 10–20 minutes of pure startup and image-pulling overhead before the actual computation is complete.

MLflow has the opposite profile. It can run locally or on a single server with no Kubernetes cluster, and its basic tracking service is lightweight. But MLflow does not natively provide the job scheduling, container orchestration, or cluster-level CPU and GPU allocation that Kubeflow provides.

That is the central point in this MLOps framework comparison: MLflow and Kubeflow are not two implementations of the same execution engine. They solve overlapping parts of the ML lifecycle with very different infrastructure assumptions.

The architectural divide: pod-per-step versus lightweight metadata

The performance gap starts with the execution model.

Kubeflow Pipelines treats a pipeline as a collection of containerized components. In KFP v2, Argo Workflows coordinates execution, and each component normally runs in its own Kubernetes pod. This gives every step a clean runtime boundary:

  • The component can use its own container image.
  • CPU, memory, and GPU requests can be assigned per step.
  • Dependencies are isolated from other components.
  • Kubernetes can schedule work across a cluster.
  • Failed steps can be retried without necessarily rerunning the entire pipeline.

Those properties are useful when the pipeline contains expensive or heterogeneous work. A GPU-heavy training step can request a GPU, while a small validation step can use a fraction of a CPU. A preprocessing component can use a different image from the model-serving component. The platform makes these boundaries explicit.

The gotcha is that the boundary is not free. Kubernetes must create and schedule a pod, attach the necessary volumes and configuration, pull an image if it is not already cached, start the container, and report its state back to the workflow engine. For a multi-hour training job, this setup time may be irrelevant. For a 400-millisecond feature transformation, it can dominate the runtime.

MLflow has a much smaller default execution footprint. You can install it with pip, start tracking locally, and log parameters, metrics, artifacts, and model versions without deploying a Kubernetes-native control plane. Its metadata service moved from Flask to FastAPI and Uvicorn in late 2024, improving the service foundation, but the more important performance characteristic remains architectural: MLflow is not creating a Kubernetes pod for every pipeline operation.

That makes MLflow attractive for development, experimentation, model registry workflows, and lightweight production services. It also creates a clear limitation. MLflow does not become a cluster scheduler simply because it tracks a run or packages a model. If the workflow needs queueing, resource-aware placement, retry policies, node selection, or GPU allocation, another execution layer must provide those functions.

MLflow avoids orchestration overhead by doing less orchestration. Kubeflow spends more resources to provide stronger execution boundaries.

This distinction matters when teams compare “framework performance.” A lower latency number from MLflow does not mean it has optimized the same workload. It may mean that the workload is running inside an existing process, job runner, or external orchestrator rather than passing through a pod lifecycle.

Quantifying pipeline execution latency

For pipeline latency, we need to separate three different numbers:

1. The time required by the actual code.

2. The time required to start and schedule the execution environment.

3. The time required to move data and artifacts between steps.

Only the first number belongs directly to the Python, Spark, SQL, or training code. Kubeflow adds the second by design. Depending on the pipeline, it can also expose the third more clearly because each step has an isolated container and may exchange data through object storage or mounted volumes.

Kubeflow’s startup tax

The reported cold-start overhead for a Kubeflow Pipelines step is approximately 30–60 seconds. Warm nodes can reduce that figure to under 20 seconds, particularly when the image is already available and the cluster has spare capacity. The exact number depends on the Kubernetes environment, image size, node state, scheduling pressure, storage configuration, and network path to the registry.

The important engineering detail is accumulation. Suppose a pipeline contains 20 sequential preprocessing steps, each doing only a small amount of work. Even if the components are logically well designed, the pod-per-step model can add 10–20 minutes of startup and image-pulling overhead. The pipeline is not “slow” because the transformation itself is inefficient. It is slow because the execution granularity is too fine for the orchestration layer.

This is a common failure mode in early Kubeflow deployments. Teams split a notebook into many small components because the component boundaries look clean in the pipeline graph. The graph is readable, but the runtime pays for every boundary.

A practical sanity check is to compare component duration with component startup time:

  • If a step runs for 30 minutes, a 30-second startup is usually acceptable.
  • If a step runs for 10 seconds, a 30-second startup is a major part of the total cost.
  • If a step runs for less than a second, placing it in its own pod is usually the wrong execution granularity.

This does not mean we should merge every Kubeflow component into one large container. Large components reduce observability and make retries more expensive. The better workaround is to place meaningful units of work behind each boundary—data extraction, feature generation, training, evaluation, and registration—rather than splitting every helper function into a separate component.

MLflow’s lower overhead, with a different responsibility

A basic MLflow deployment can run on a local machine or a single server. There is no initial Kubernetes control plane to install, and the tracking service does not need to schedule a separate pod for each logged operation. That gives MLflow a much smaller infrastructure footprint for short-running workflows.

MLflow’s AI Gateway, now part of the MLflow AI Platform, adds approximately 3 milliseconds over a direct LLM call for configuration resolution, secret decryption, and provider dispatch. That number is useful as an example of a lightweight service hop: the gateway adds a small amount of processing without introducing a container lifecycle for each request.

For tracing, MLflow supports asynchronous trace logging. Spans can be buffered and uploaded in the background instead of making the user-facing application wait for every telemetry write. This is the right pattern for production inference paths—observability should not become a hidden synchronous dependency in the request critical path.

However, we should not interpret low MLflow overhead as proof that MLflow executes a full pipeline faster than Kubeflow under identical conditions. There is no established head-to-head benchmark here for MLflow Recipes versus Kubeflow Pipelines on the same hardware and workload. The comparison is architectural, not a universal throughput claim.

A useful comparison table

ParameterMLflowKubeflow Pipelines
Default execution modelLightweight tracking and lifecycle services; execution can remain in an existing process or external runnerContainerized pipeline components executed through Kubernetes and Argo Workflows
Kubernetes requirementNo; MLflow can run locally or on a single serverYes for the standard Kubernetes-native deployment model
Per-step pod startupNot inherent to MLflowApproximately 30–60 seconds cold-start overhead per step; under 20 seconds on warm nodes in favorable conditions
Resource schedulingNot natively provided at cluster levelNative CPU, memory, and GPU requests and limits per component
Best fit for short sequential tasksLow platform overheadCan be inefficient when many steps perform little work
IsolationDepends on the selected runner and deployment designStrong container and pod boundaries by default
Pipeline orchestrationRequires an additional mechanism or a feature-specific workflow layerCore capability of the platform
Infrastructure footprintSmall initial footprintSubstantial Kubernetes-based deployment; a minimal development setup requires at least 8 vCPUs and 32 GiB RAM
Common operational riskTeams assume tracking equals schedulingIncorrect resource requests can leave pods Pending indefinitely

Infrastructure footprint and CPU overhead

Latency is visible in the pipeline timeline. Resource overhead is easier to miss because it appears in cluster utilization, idle services, control-plane activity, and operational maintenance.

A minimal Kubeflow development setup requires at least 8 vCPUs and 32 GiB of RAM. A full deployment can run approximately 30 pods in the Kubeflow namespace alone. These resources are not necessarily consumed by the training job. They support the platform: workflow services, metadata, UI, controllers, databases, caches, and other components required to operate the environment.

That baseline changes the economics of experimentation. If a team needs one small development pipeline, running Kubeflow may cost more in infrastructure and operator attention than the workload itself. If the organization already operates Kubernetes and needs repeatable multi-tenant execution, the same platform footprint may be justified.

The phrase “CPU overhead” needs careful handling here. Kubeflow’s pod-per-step architecture creates overhead from control-plane operations, sidecars, container startup, image management, and platform services. But a single universal CPU percentage is not available from the supplied measurements. The actual figure depends on cluster size, pod density, image behavior, workflow concurrency, metadata writes, and the duration of the underlying tasks.

MLflow is lighter, but “lightweight” does not mean “free under every load.” A tracking server still handles requests, metadata writes, artifact references, authentication, and possibly trace ingestion. The exact CPU utilization under high concurrent write loads depends on the deployment and workload. We should avoid presenting an unsupported percentage as a benchmark.

The more reliable comparison is the minimum operational surface:

What consumes resources in each design

MLflow typically requires:

  • A tracking or metadata service.
  • A backend store for run metadata.
  • Artifact storage for models and files.
  • Optional model registry and serving components.
  • An external job runner or orchestrator when workflows need scheduling.

Kubeflow typically adds:

  • Kubernetes control-plane and worker capacity.
  • Argo Workflows for pipeline execution.
  • Multiple platform services and controllers.
  • Container images and registries.
  • Cluster networking, storage, secrets, and identity integration.
  • Resource quotas and scheduling policies.
  • Monitoring and operational tooling for the platform itself.

The tradeoff is straightforward. MLflow minimizes the platform you must operate, but it also leaves execution policy outside the framework. Kubeflow centralizes more of the execution policy, and therefore requires more infrastructure to keep that policy running.

The cheapest pipeline step is not always the one with the fastest code. It is the one whose execution boundary matches the amount of work being done.

Why Kubeflow steps get stuck in Pending

Kubeflow’s resource model is one of its strongest capabilities—and one of its most common operational traps.

Each component can request CPU, memory, and GPU resources, and can also define limits. Kubernetes uses those requests to decide where the pod can run. If the request does not fit any available node, the pod remains in a Pending state. The pipeline may look healthy from the control-plane perspective while the actual work never starts.

This often happens for reasons that are not obvious from the component code:

  • A preprocessing step requests a GPU because the component template was copied from a training step.
  • A pod requests more memory than any node can provide, even though average memory use is low.
  • A node has enough aggregate capacity but not enough allocatable capacity after system and DaemonSet reservations.
  • The required GPU type exists in the cluster, but its taints or labels exclude the pod.
  • The container image requires a node architecture or runtime that is not available.
  • Several steps start concurrently and consume the capacity needed by a later step.

The workaround is not to remove resource requests. It is to make them representative.

Start with measured usage from a realistic run, then set requests close to the resources needed for scheduling and limits according to the failure policy of the workload. Training jobs may need headroom for data loading or temporary tensors. A small validation step usually does not need the same memory profile as a distributed trainer.

For every component, we should be able to answer:

1. Does this step genuinely need a GPU?

2. What is its peak memory requirement, not just its average?

3. Can it run on the available node pools?

4. Will its image already be cached, or will every run pull it?

5. Is the step long enough to justify a separate pod?

6. What happens if the step is retried?

This is where Kubeflow becomes valuable for serious production pipelines. The scheduler can enforce resource boundaries that MLflow alone does not provide. But the team must operate those boundaries correctly. A resource request is not a comment. It is a scheduling constraint.

Optimizing pipeline latency in production

The largest Kubeflow performance gains usually come from changing the shape of the workflow rather than tuning a single Python function.

1. Increase the amount of work per component

Combine tiny sequential preprocessing operations when they share the same image, dependencies, and failure behavior. Ten small components may be easier to draw but slower to execute than one component that performs the same transformations in a single container.

We should preserve boundaries where they carry operational value:

  • Different resource profiles.
  • Independent retry behavior.
  • Separate ownership.
  • Reusable outputs.
  • Clear data contracts.
  • Distinct security requirements.

If a boundary exists only because one function was copied from a notebook cell, it probably does not deserve a Kubernetes pod.

2. Keep images small and predictable

Image pulling is part of pipeline latency. A large general-purpose image can make every short step expensive, especially on a new node. Use focused base images, remove build-time caches, and avoid bundling unrelated frameworks into every component.

A small image does not eliminate scheduling overhead, but it reduces the portion caused by registry access and startup initialization. Warm nodes help too, but relying on warm caches alone is fragile—autoscaling and node replacement will expose the cold path eventually.

3. Avoid unnecessary sequential dependencies

A pipeline graph that serializes independent work forces every step to wait for the previous one. If feature extraction and data-quality checks can run concurrently, model them as parallel branches and join them only when the result is needed.

Parallelism must match cluster capacity. Launching more pods does not automatically reduce wall-clock time if every pod competes for the same CPU, memory, storage bandwidth, or GPU pool. We need to inspect both the workflow graph and the scheduler state.

4. Use MLflow where lifecycle tracking is the real requirement

Many teams deploy Kubeflow because they need experiment tracking, model registration, and reproducibility, then discover that most of their jobs already run in an existing CI system, batch scheduler, or cloud service.

In that case, MLflow may be the cleaner layer. It can record the run, store artifacts, register models, and support deployment workflows without introducing a second scheduler. The external system remains responsible for execution.

This design is especially effective for:

  • Notebook-to-training transitions.
  • Scheduled jobs already managed by Airflow or a cloud scheduler.
  • CI/CD workflows that build and launch containers elsewhere.
  • Small inference services where request latency matters more than pipeline graph management.
  • Teams without a dedicated Kubernetes platform group.

The tradeoff is that the architecture becomes more distributed. We need clear ownership of retries, logs, resource allocation, secrets, and failure states. MLflow records the lifecycle; it does not automatically coordinate every part of it.

5. Keep telemetry off the critical path

Tracing and metric logging should be asynchronous where possible. MLflow’s asynchronous trace logging support is designed for this pattern: buffer spans, then upload them in the background.

The same principle applies to Kubeflow components. Avoid turning every metadata write, artifact upload, or progress update into a blocking dependency for the main computation. A pipeline should not spend its critical path waiting for observability infrastructure unless the metadata is required to make a correctness decision.

Choosing between MLflow and Kubeflow

The decision is less about which framework is “faster” and more about which cost we are willing to pay.

Choose MLflow when the priority is a low-overhead ML lifecycle layer. It is a strong fit when we need experiment tracking, artifact management, model registry, and deployment metadata without Kubernetes-native orchestration. It can run on a single server and integrate with the execution system the organization already uses.

Choose Kubeflow Pipelines when the priority is repeatable, containerized, resource-aware execution across a Kubernetes cluster. It is a better fit when workflows need per-step CPU, memory, or GPU requests; strong isolation; cluster scheduling; parallel execution; and platform-level pipeline management.

The following decision pattern is usually more useful than a feature checklist:

  • Short steps, few dependencies, existing job runner: use MLflow with the existing execution system.
  • Long-running training and evaluation components: Kubeflow’s startup overhead is easier to justify.
  • GPU scheduling and heterogeneous node pools: Kubeflow provides the native resource model MLflow lacks.
  • Single-server development or early experimentation: start with MLflow and avoid unnecessary cluster overhead.
  • Many tiny pipeline components: redesign the granularity before adopting or expanding Kubeflow.
  • Strict container isolation and repeatable environments: Kubeflow’s pod-per-step model is an advantage.
  • Low-latency inference requests: keep request handling separate from heavyweight orchestration and use asynchronous telemetry.

There is also a valid hybrid design. MLflow can manage experiments, artifacts, model versions, and deployment metadata while Kubeflow executes resource-intensive pipelines. In that arrangement, we should define the boundary explicitly: Kubeflow owns scheduling and execution; MLflow owns the model lifecycle and evidence generated by each run.

The main risk is allowing the boundary to remain implicit. If both systems appear to own retries, artifacts, metadata, or deployment state, debugging becomes slower than either platform’s startup overhead.

A practical operating checklist

Before committing to a pipeline design, we should measure the workload at the same granularity that the platform will execute it.

  • Record actual component runtime separately from pod startup and image-pull time.
  • Test both cold-node and warm-node behavior; production autoscaling will eventually produce cold starts.
  • Group short, tightly coupled steps into a single component.
  • Keep resource requests close to measured peak requirements.
  • Verify that every requested GPU, CPU range, and memory size can be satisfied by an available node pool.
  • Inspect Pending pods through Kubernetes scheduling events rather than treating them as application failures.
  • Use small, purpose-built images for frequently executed components.
  • Check whether parallel steps are actually improving wall-clock time or merely increasing contention.
  • Keep tracing and metadata uploads asynchronous on latency-sensitive paths.
  • Decide which system owns scheduling, retries, artifacts, model versions, and deployment state.
  • Benchmark the full workflow, not just the model code.
  • Treat infrastructure startup as part of the user-visible pipeline latency.

The final choice in this MLOps framework comparison is therefore operational. MLflow is the better default when the team wants a lightweight lifecycle layer and already has a way to run jobs. Kubeflow earns its footprint when Kubernetes-native orchestration, resource scheduling, and isolated execution are central requirements.

For short sequential tasks, Kubeflow’s pod-per-step architecture can turn orchestration into the dominant cost. For substantial, heterogeneous workloads, that same architecture provides the control needed to run production pipelines reliably. The practical answer is to match the execution boundary to the work—and to stop giving every ten-second function its own pod.

FAQ

Why is my Kubeflow pipeline slow despite the code running quickly?
The pipeline is likely suffering from orchestration overhead, where the time spent creating pods, pulling images, and scheduling resources for many small steps exceeds the actual computation time.
Does MLflow provide the same resource scheduling as Kubeflow?
No, MLflow does not natively provide cluster-level CPU, GPU, or memory allocation; it requires an external execution layer if those scheduling functions are needed.
What is the minimum infrastructure required for Kubeflow?
A minimal Kubeflow development setup requires at least 8 vCPUs and 32 GiB of RAM to support the platform services, controllers, and metadata components.
How can I reduce startup latency in Kubeflow pipelines?
You can reduce latency by combining tiny sequential tasks into single components, using smaller container images, and ensuring nodes are warm to leverage cached images.
Why do my Kubeflow pods stay in a Pending state?
Pods remain in a Pending state when the requested CPU, memory, or GPU resources cannot be satisfied by any available node in the cluster, often due to overly restrictive or inaccurate resource requests.