Practical MLOps · Serving and inference layer
Production ML Model Serving: FastAPI, Ray Serve, Triton or KServe
The controlled path from a versioned model artifact to a measurable online or batch service. The right runtime depends on latency, throughput, batching, hardware, rollout, and operational ownership — not on which tool is most popular.
Direct answer
Production model serving is the controlled path from a versioned model artifact to a measurable online or batch service. FastAPI is useful for a thin application API, Ray Serve for Python-native composition and scaling, Triton for optimized multi-framework inference, and KServe for Kubernetes-native serving workflows. The right choice depends on latency, throughput, batching, hardware, rollout, and operational ownership.
Serving architecture and artifact-to-runtime contract
- GatewayTLS, auth, rate limiting
- ValidationSchema check, payload size
- PreprocessingNormalize, tokenize, resize
- InferenceModel forward pass (batched)
- PostprocessingDecode, threshold, format
- Response & TelemetryReturn result; export latency, error, count
Every serving runtime implements this path. The difference is how much of it the runtime handles for you and how much you build.
Artifact-to-runtime contract
Inputs
- Model artifact from MLflow registry (via @champion alias)
- Container image with runtime and dependencies
- Serving configuration (replicas, batching, resources)
- Input schema (model signature)
Outputs
- Predictions via HTTP or gRPC
- Operational telemetry (latency, error rate, throughput)
- Health and readiness signals
State owned
- Loaded model instances in memory
- Request queue and batching state
- Replica count and autoscaling state
Failure boundary
The serving runtime does not own model quality, data quality, or the decision to promote. It serves what the registry points to and reports what it observes. If the model is wrong, the runtime serves wrong answers quickly and reliably.
Choosing a production model-serving runtime
No runtime is best. Make the decision falsifiable through workload requirements.
FastAPI
- Choose when
- You need a thin application API around a model, you are comfortable building batching and scaling yourself, and your model is a Python object that loads in-process.
- Avoid when
- You need dynamic batching, multi-GPU inference, Kubernetes-native autoscaling, or production-grade fault recovery without building it.
- Operational tradeoff
- Maximum flexibility and minimal abstraction. You own everything: batching, scaling, health checks, reload behavior. Fast to prototype, expensive to harden.
Ray Serve
- Choose when
- You want Python-native composition (multiple models, pre/post-processing as separate deployments), need scaling and fault tolerance, and your team is Python-first.
- Avoid when
- You need maximum GPU inference throughput for multi-framework models, or your team prefers Kubernetes-native configuration over Python deployment configs.
- Operational tradeoff
- Composable Python-native serving with built-in scaling and fault tolerance. Production deployment via KubeRay RayService CR on Kubernetes. Less optimized for raw GPU throughput than Triton.
NVIDIA Triton Inference Server
- Choose when
- You need optimized multi-framework inference (TensorFlow, PyTorch, ONNX, TensorRT), dynamic batching, concurrent model instances on GPUs, and you have GPU hardware.
- Avoid when
- You are serving on CPU only, your model is a simple Python object, or you do not need GPU-level throughput optimization.
- Operational tradeoff
- Highest inference throughput with dynamic batching and instance groups. Configured via config.pbtxt, not Python code. Requires GPU hardware and NVIDIA container infrastructure.
KServe
- Choose when
- You want Kubernetes-native serving with a standard CRD (InferenceService), canary rollouts, A/B testing, scale-to-zero, and multi-framework support without managing individual deployments.
- Avoid when
- You are not on Kubernetes, or you need fine-grained control over batching and GPU instance groups that KServe's abstraction does not expose.
- Operational tradeoff
- Kubernetes-native abstraction over serving infrastructure. Canary rollout via canaryTrafficPercent is only supported in serverless (Knative) deployment mode. Scale-to-zero and KPA autoscaling require Knative deployment. Standard Kubernetes deployment provides full resource control but does not automatically provide canary rollout or scale-to-zero. Choose the deployment mode based on your workload requirements.
TorchServe (maintenance-status-aware)
- Choose when
- Current official repository evidence supports its active maintenance and you need PyTorch-specific serving with torchserve conventions.
- Avoid when
- The repository shows maintenance concerns, or you need multi-framework support.
- Operational tradeoff
- PyTorch-native, but check the official GitHub repository for current maintenance status before selecting. KServe and Ray Serve both support PyTorch models with broader ecosystems.
Online, asynchronous, streaming, and batch serving boundaries
Online serving returns a prediction synchronously within a latency budget (milliseconds to seconds). The client waits. This is the default for user-facing applications.
Asynchronous serving accepts a request and returns a job ID; the client polls or receives a callback when the result is ready. Use this when inference takes longer than a synchronous timeout (large models, batch processing within a request).
Streaming serving returns results incrementally — token by token for LLMs, or frame by frame for video. The connection stays open and results flow as they are produced.
Batch serving processes a dataset offline and writes results to storage. There is no online client. Use this for recurring scoring jobs, backfills, and reports. Batch serving does not need a serving runtime — it can be a pipeline step.
Dynamic batching, concurrency, and backpressure
Dynamic batching combines multiple inference requests into a single batch to maximize throughput. NVIDIA Triton enables it via config.pbtxt with dynamic_batching { }. The max_queue_delay_microseconds setting controls how long the scheduler waits to collect more requests before launching the batch.
Concurrent model execution uses instance_group in Triton's config to spawn multiple model instances on the same or different GPUs. This is useful when a single instance cannot saturate the GPU or when models in an ensemble have different throughputs.
Backpressure is the signal that the serving runtime is at capacity. A growing request queue, rising p95 latency, or rejected connections are backpressure signals. The response is to scale replicas, enable or tune dynamic batching, or shed load. Ignoring backpressure leads to OOM and cascading failures.
- Dynamic batching: Triton config.pbtxt → dynamic_batching { }
- Queue delay: max_queue_delay_microseconds controls batch collection window
- Instance groups: instance_group [{ count: N, kind: KIND_GPU, gpus: [0, 1] }]
- Backpressure signals: queue growth, p95 rise, connection rejection
- Backpressure response: scale replicas, tune batching, shed load
Health checks, autoscaling and cold starts
Autoscaling should be driven by the signal that matters to users: latency. Scaling on CPU alone misses GPU-bound workloads where CPU is idle but inference is saturated. Scale on p95 latency, queue depth, or request rate — whichever reflects the bottleneck.
Cold-start is the latency of a new replica loading the model. For large models (GBs), cold-start can be minutes. Scale early enough that a traffic spike does not wait for cold-starts, or keep warm replicas above the minimum.
KServe supports scale-to-zero for predictive workloads, which saves cost but introduces cold-start on the first request. Use scale-to-zero for low-traffic models where cold-start is acceptable, not for latency-critical user-facing endpoints.
Health checks, autoscaling and cold starts
Readiness means the model is loaded and the runtime can accept requests. A readiness check failing removes the replica from the load balancer without killing it. Liveness means the process is healthy; a liveness check failing restarts the pod.
Dependency health checks cover downstream services the runtime depends on: the model registry, the feature store, the monitoring backend. If a dependency is down, the runtime should report not-ready rather than serving errors.
Timeouts and retries should be set on the client side and the runtime side. A client timeout longer than the runtime's latency budget wastes resources. A retry without backpressure amplifies load during an incident. Use circuit breakers to stop sending traffic to a failing downstream rather than retrying indefinitely.
Canary, shadow, blue/green, and rollback patterns
Canary release routes a small percentage of traffic to the new model version while the rest stays on the current version. If the canary's metrics (error rate, latency, business outcome) regress, traffic is rolled back. KServe supports canary rollouts via the canaryTrafficPercent field in the InferenceService spec, but only in serverless (Knative) deployment mode. Standard Kubernetes deployment does not provide this capability.
Shadow release sends the same traffic to both versions but only returns the current version's response. The new version's predictions are logged and compared offline. This tests the new model without user impact.
Blue/green deployment runs two full environments (blue = current, green = new). Traffic is switched from blue to green atomically. If the green environment fails, traffic switches back to blue. This requires double the resources during the switch.
Rollback means redirecting traffic back to the previous version. With MLflow aliases, this can be alias reassignment (@champion back to the previous version). With KServe, it is reverting the canary split. The rollback path must be tested before you need it.
Benchmark methodology and serving failures
Benchmark numbers are not published on this page. When benchmarks are run, they must include the following to be credible:
- Exact hardware (GPU model, CPU, memory, network)
- Software versions (runtime, model framework, CUDA)
- Model and input shape (e.g. ResNet-50, 224x224x3, batch 8)
- Warm-up period before measurement
- Concurrency range tested (e.g. 2 to 16 concurrent requests)
- p50, p95, p99 latency
- Throughput (inferences per second)
- Error rate
- Batch configuration (dynamic batching on/off, max batch size)
- Measurement tool (e.g. Triton Performance Analyzer, wrk, locust)
- Date and code commit
How serving fails and recovers
| Failure | Observable signal | Likely cause | First diagnostic | Containment | Durable fix | Evidence |
|---|---|---|---|---|---|---|
| Schema mismatch on input | Runtime returns 400 Bad Request; preprocessing error in logs | Client sent different schema than model signature; model retrained with new features | Compare request schema against MLflow model signature | Reject the request with a clear error message | Version the model signature; validate at gateway before forwarding to runtime | Engineering inference from serving best practices |
| Out of memory (OOM) | Replica killed by OOM killer; restart loop | Model too large for replica memory, or batch size exceeds memory | Check memory usage and batch size configuration | Reduce batch size or increase replica memory | Set memory limits with headroom; add OOM alerting; profile model memory | Engineering inference from Kubernetes and serving documentation |
| Saturation under load | p95 latency rises; throughput plateaus | Replicas at capacity; queue growing | Check Prometheus for queue depth, replica count, GPU utilization | Scale replicas or enable dynamic batching | Set autoscaling on p95 latency, not just CPU; tune batching configuration | Documented from NVIDIA Triton and Ray Serve documentation |
| Queue growth without processing | Request queue grows; requests time out | All replicas unhealthy or stuck on a slow request | Check replica health and recent request traces | Restart unhealthy replicas; shed load | Add circuit breaker; set queue depth limits and rejection thresholds | Engineering inference from serving best practices |
| Dependency timeout | Requests fail with dependency error; latency spikes | Feature store, model registry, or downstream service is slow or down | Check dependency health from the runtime's perspective | Fail fast with a clear error; use cached features if available | Add circuit breakers for dependencies; set per-dependency timeouts | Engineering inference from serving best practices |
| Bad model load | Replica fails readiness check; model file error in logs | Artifact corrupted, wrong format, or version incompatibility | Check model artifact URI and format against runtime expectations | Roll back to previous @champion version | Add a model load test to CI before promotion | Engineering inference from MLflow and serving documentation |
| Canary regression | Canary's error rate or latency worse than baseline | New model version performs worse on real traffic than in evaluation | Compare canary vs baseline metrics in Grafana | Roll back canary to 0% traffic | Require canary observation period and automatic rollback thresholds before full promotion | Documented from KServe canary rollout documentation |
| Telemetry loss | No metrics appearing in Grafana for a runtime | Metrics exporter down, scraping misconfigured, or network policy blocking | Check Prometheus targets and exporter health | Fix exporter; use access logs as temporary fallback | Alert on metric absence; test scraping in staging | Engineering inference from Prometheus best practices |
Tested environment and limitations
Tool versions
- NVIDIA Triton: documented (config.pbtxt, dynamic batching)
- Ray Serve: 2.43+ (production guide documented)
- KServe: v0.15+ (InferenceService CRD documented)
- Triton Performance Analyzer: 23.10 (archived docs)
Known limitations
- No benchmark numbers are published on this page. Benchmark results will be published only after measurement in a documented environment with the methodology listed above.
- TorchServe maintenance status should be verified against the official GitHub repository (github.com/pytorch/serve) before selection.
- Triton Performance Analyzer documentation referenced is from archive version 23.10. Verify current version documentation for the latest measurement options.
Sources
The SourceLedger makes technical claims auditable and gives search and retrieval systems explicit source context. It does not guarantee citation, ranking or inclusion.
- https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/tutorials/Conceptual_Guide/Part_2-improving_resource_utilization/README.html01Tier 1NVIDIA
NVIDIA Triton — Dynamic Batching & Concurrent Model Execution
Paraphrased. Dynamic batching improves both latency and throughput for stateless models.
Supports claims:
- dynamic batching combines inference requests into a single batch
- configured via config.pbtxt: dynamic_batching { }
- max_queue_delay_microseconds for batch collection delay
- instance_group for concurrent model execution on GPUs
- Performance Analyzer for benchmarking
- stateless models for dynamic batching; sequence batcher for stateful
Last verified:
- https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/user_guide/model_configuration.html02Tier 1NVIDIA
NVIDIA Triton — Model Configuration
Paraphrased from official documentation.
Supports claims:
- model configuration via config.pbtxt
- instance_group, dynamic_batching, priority configuration
Last verified:
- https://docs.nvidia.com/deeplearning/triton-inference-server/archives/triton-inference-server-2310/user-guide/docs/user_guide/perf_analyzer.html03Tier 1NVIDIA
NVIDIA Triton — Performance Analyzer
Version: Triton 23.10
Archived version documentation. Used for benchmark methodology reference.
Supports claims:
- Performance Analyzer tool for measuring Triton serving performance
- concurrency-range, percentile, throughput, latency measurement
Last verified:
- https://docs.ray.io/en/latest/serve/production-guide/index.html04Tier 1Ray
Ray Serve — Production Guide
Version: Ray 2.43+
RayService custom resource automatically handles production requirements.
Supports claims:
- production deployment via KubeRay RayService CR on Kubernetes
- serve build / serve deploy / serve status workflow
- config file as single source of truth for deployment
- health checking, status reporting, failure recovery, upgrades handled by RayService
- deploy on VMs as alternative to Kubernetes
Last verified:
- https://docs.ray.io/en/latest/serve/production-guide/fault-tolerance.html05Tier 1Ray
Ray Serve — Fault Tolerance
Paraphrased from official documentation.
Supports claims:
- Serve deployment fault tolerance and recovery
- replica failure handling
Last verified:
- https://kserve.github.io/website/06Tier 1KServe
KServe Documentation
Version: KServe v0.20
Paraphrased. Canary rollout, scale-to-zero, and KPA autoscaling are deployment-mode dependent — see kserve-canary, kserve-kpa-autoscaler, and kserve-admin-guide sources. Do not present these as universal KServe behavior.
Supports claims:
- KServe as Kubernetes CRD for serving ML models (InferenceService)
- multi-framework support (TensorFlow, PyTorch, scikit-learn, XGBoost, ONNX)
- control plane: model revision tracking
- data plane: standardized inference protocol
- KServe joined CNCF as incubating project (November 2025)
Last verified:
- https://github.com/pytorch/serve07Tier 1PyTorch / GitHub
TorchServe Repository
Check repository for current maintenance status before recommending. Include only if current official evidence supports it.
Supports claims:
- TorchServe repository and current maintenance status
Last verified:
- https://mlflow.org/docs/latest/ml/model-registry/08Tier 1MLflow
MLflow Model Registry
Version: MLflow 3.x
Aliases are mutable named references to model versions, useful for deployment promotion. OSS registry provides UI + API; Databricks extends with Unity Catalog.
Supports claims:
- model registration and versioning
- model aliases (e.g. @champion)
- model lifecycle: staging, production
- tags for governance
- lineage to MLflow runs
- Databricks Unity Catalog integration for governance
Last verified:
- https://kserve.github.io/website/docs/0.18/model-serving/predictive-inference/rollout-strategies/canary09Tier 1KServe
KServe Canary Rollout Strategy
Version: KServe v0.18+
Paraphrased. Official documentation states: 'Canary rollout strategy is only supported in serverless deployment mode.' Standard Kubernetes deployment does not provide canary rollout via canaryTrafficPercent.
Supports claims:
- canary rollout via canaryTrafficPercent field
- canary rollout is ONLY supported in serverless (Knative) deployment mode
- KServe tracks last good revision for automatic rollback
- bad revisions do not receive traffic
- rollback pins 100% traffic to previous good revision
Last verified:
- https://kserve.github.io/website/docs/model-serving/predictive-inference/autoscaling/kpa-autoscaler10Tier 1KServe
KServe Autoscaling with Knative Pod Autoscaler
Version: KServe v0.20
Paraphrased. KPA autoscaler is NOT available in standard Kubernetes deployment mode. Scale-to-zero is a Knative/serverless feature, not a universal KServe capability.
Supports claims:
- KPA (Knative Pod Autoscaler) is only supported in Knative deployment mode
- scale-to-zero requires minReplicas: 0 and Knative/serverless deployment
- autoscaling metrics: concurrency, rps, cpu, memory
- cold-start cost when pods scale from zero
- containerConcurrency as a hard limit on simultaneous requests
- 60-second stable window and 6-second panic window for autoscaling
Last verified:
- https://kserve.github.io/website/docs/admin-guide/overview11Tier 1KServe
KServe Administrator Guide — Deployment Modes
Version: KServe v0.20
Paraphrased. Deployment mode determines available features. Standard Kubernetes deployment does NOT automatically provide scale-to-zero, KPA autoscaling, or canary rollout.
Supports claims:
- three deployment modes: Standard Kubernetes, Knative/Serverless, LLMInferenceService
- Standard deployment: full resource control, GPU workloads, production
- Knative/Serverless deployment: scale-to-zero, burst/unpredictable traffic
- LLMInferenceService: advanced LLM features (prefix routing, disaggregated serving)
- InferenceService (Standard) works for all workloads
- Gateway API recommended for generative inference streaming
Last verified:
Deploy, measure and troubleshoot the serving path
Move from a comparison table to a working service with health checks, load testing, observability and a tested rollback path.
- Deploy a versioned model service.
- Measure latency, throughput and errors.
- Diagnose saturation and deployment failures.