Skip to content
16px
KServeKubernetesModel ServingMLOpsLLM Infrastructure

KServe on Kubernetes: What Actually Happens After You Apply an InferenceService

A senior-engineer walkthrough of KServe's control plane, generated Kubernetes resources, model loading, networking, autoscaling, canary releases, and debugging.

July 14, 202621 min read

When engineers first see KServe, it can look almost too simple.

You write a small YAML file:

yaml
1apiVersion: serving.kserve.io/v1beta1
2kind: InferenceService
3metadata:
4  name: my-model
5spec:
6  predictor:
7    model:
8      modelFormat:
9        name: huggingface
10      storageUri: s3://models/my-model

Then you run:

bash
1kubectl apply -f inference-service.yaml

A few moments later, the model has an endpoint, traffic routing, health checks, autoscaling, and possibly canary deployment support.

That convenience is useful, but it creates a dangerous misunderstanding:

KServe is not replacing Kubernetes. KServe is generating and managing Kubernetes resources for you.

When the service breaks, the problem is rarely solved by staring at the original InferenceService YAML for an hour. You still need to understand the Deployments, Pods, Services, init containers, networking resources, autoscalers, revisions, and model server processes underneath it.

This article builds that mental model.

The First Mental Model: KServe Is an Operator, Not Just a Model Server

A junior engineer may initially think of KServe as another inference server like vLLM, Triton, TensorFlow Serving, or TorchServe.

That is not quite correct.

Those systems are primarily model-serving runtimes. They load a model and execute inference.

KServe is a model-serving platform and Kubernetes control plane. It can select or run model-serving runtimes, but its larger responsibility is managing the infrastructure around those runtimes.

A useful way to think about it is:

Your InferenceService YAML
          |
          v
KServe controller
          |
          v
Generated Kubernetes serving stack
          |
          +--> Deployment / Knative Service
          +--> Pods
          +--> Kubernetes Service
          +--> Gateway or Ingress configuration
          +--> Autoscaling resources
          +--> Model storage initialization
          +--> Traffic splitting and revisions

KServe behaves like a compiler combined with a continuously running operations engineer:

  1. You declare the desired state.
  2. The controller translates that declaration into lower-level resources.
  3. The controller continuously compares the desired state with the actual state.
  4. If something changes or disappears, it attempts to reconcile the system.

This reconciliation loop is the foundation of nearly every Kubernetes operator.

What You Write: The `InferenceService` Custom Resource

Kubernetes understands built-in objects such as:

  • Deployment
  • Service
  • ConfigMap
  • Secret
  • HorizontalPodAutoscaler

KServe adds new Kubernetes object types through Custom Resource Definitions, or CRDs. One of the most important is InferenceService.

Instead of manually describing every infrastructure object, you describe the model-serving intent.

For example:

yaml
1apiVersion: serving.kserve.io/v1beta1
2kind: InferenceService
3metadata:
4  name: fraud-detector
5  namespace: ml-production
6spec:
7  predictor:
8    minReplicas: 1
9    model:
10      modelFormat:
11        name: sklearn
12      protocolVersion: v2
13      storageUri: s3://company-models/fraud-detector/v17
14      resources:
15        requests:
16          cpu: "500m"
17          memory: "1Gi"
18        limits:
19          cpu: "2"
20          memory: "4Gi"

This specification communicates several important decisions:

  • The serving workload is named fraud-detector.
  • It should run in the ml-production namespace.
  • The model format is scikit-learn.
  • The model artifacts are stored in S3.
  • The runtime should expose the V2 inference protocol.
  • The predictor should have explicit CPU and memory requests and limits.
  • At least one replica should remain available.

Notice what is missing.

You did not directly define:

  • A Pod template
  • Container ports
  • Readiness probes
  • A Kubernetes Service
  • External routing
  • A storage-download init container
  • Autoscaler configuration
  • Runtime image selection

KServe fills in those details using the InferenceService, KServe configuration, and the selected ServingRuntime.

That abstraction is the main value of KServe.

What the KServe Controller Does

After kubectl apply, the Kubernetes API server stores the InferenceService object.

The KServe controller watches for objects of this type.

Conceptually, its reconciliation logic looks like this:

Observe InferenceService
        |
        v
Validate requested model format and runtime
        |
        v
Resolve storage, containers, resources, and networking
        |
        v
Create or update lower-level Kubernetes resources
        |
        v
Observe their status
        |
        v
Update InferenceService status
        |
        v
Repeat whenever desired or actual state changes

This is important because applying an InferenceService does not synchronously deploy a healthy model.

kubectl apply only confirms that Kubernetes accepted the desired-state object.

The actual deployment can still fail later because of:

  • Invalid runtime configuration
  • Missing storage credentials
  • An inaccessible model URI
  • Image-pull failures
  • Insufficient CPU, memory, or GPU capacity
  • Readiness probe failures
  • Gateway or DNS problems
  • Autoscaler configuration errors

A successful kubectl apply means:

The request was accepted.

It does not mean:

The model is ready to serve production traffic.

Always check the status afterward:

bash
1kubectl get inferenceservice fraud-detector -n ml-production

For deeper information:

bash
1kubectl describe inferenceservice fraud-detector -n ml-production

And for the raw status conditions:

bash
1kubectl get inferenceservice fraud-detector \
2  -n ml-production \
3  -o yaml

The Generated Serving Stack

The diagram shows a generated serving stack per model. That is the correct mental model, but the exact resources depend on the KServe deployment mode.

At a high level, the generated stack contains four important areas:

  1. Networking
  2. Predictor runtime
  3. Model storage initialization
  4. Autoscaling and release management

Let us examine each one.

1. Networking: Gateway, Ingress, and Internal Services

Your application needs a stable way to reach the model.

KServe creates or coordinates the networking layer that routes a request toward the predictor workload.

A simplified path looks like this:

Client
  |
  v
Load Balancer / Gateway
  |
  v
Kubernetes routing resource
  |
  v
Service
  |
  v
Predictor Pod

Depending on how the cluster is installed, this may involve:

  • Kubernetes Gateway API
  • A traditional Kubernetes Ingress
  • Knative networking
  • Istio, Kourier, Contour, or another implementation
  • Internal Kubernetes Services

The important lesson is that an inference endpoint is not magic. It is still normal network routing.

If the endpoint returns 404, 503, or times out, inspect the networking chain instead of assuming the model code is broken.

Useful commands include:

bash
1kubectl get service -n ml-production
2kubectl get gateway -A
3kubectl get httproute -A
4kubectl get ingress -n ml-production
5kubectl get endpoints -n ml-production

Questions to ask:

  • Does the external route exist?
  • Does it point to the correct Service?
  • Does the Service have healthy endpoints?
  • Are the expected ports aligned?
  • Is the host header correct?
  • Is TLS termination configured correctly?
  • Is a network policy blocking traffic?
  • Is the gateway controller healthy?

A model can be perfectly loaded while its endpoint is completely unreachable.

2. The Predictor: The Process That Actually Runs Inference

The predictor is the part that receives the request and executes the model.

KServe may run a built-in runtime or a custom container. Common underlying runtimes include systems such as:

  • vLLM
  • NVIDIA Triton Inference Server
  • MLServer
  • TensorFlow Serving
  • Hugging Face-based runtimes
  • Custom model servers

A useful separation is:

KServe
  = orchestration and platform layer

vLLM / Triton / MLServer / custom server
  = request execution and model runtime

Suppose you deploy an LLM using a KServe Hugging Face runtime backed by vLLM.

KServe may manage:

  • Deployment lifecycle
  • Service discovery
  • Model storage integration
  • Routing
  • Autoscaling configuration
  • Status reporting

vLLM manages:

  • Loading model weights
  • GPU memory use
  • Token generation
  • Continuous batching
  • KV-cache behavior
  • Request scheduling inside the model server

This separation helps during debugging.

For example:

SymptomLikely layer
InferenceService was never reconciledKServe control plane
Pod is stuck in PendingKubernetes scheduling or capacity
Model download failsStorage initializer or credentials
Container starts but model load crashesModel runtime
Endpoint gives 404Gateway, route, host, or path
Endpoint gives 503No ready backend, scaling, or networking
High time-to-first-tokenRuntime configuration, batching, GPU, or model size
Requests are routed to the wrong revisionCanary or routing configuration

Do not debug every failure as though it belongs to KServe itself.

3. Model Storage: Why the Storage Initializer Matters

The diagram shows the predictor pulling a model from S3 before the model server starts.

KServe commonly handles this using a storage initializer, which runs as an init container.

A Kubernetes init container must complete successfully before the main application container starts.

The lifecycle is roughly:

Pod scheduled
    |
    v
Storage initializer starts
    |
    v
Authenticate to S3 / GCS / HTTP / Hugging Face / other source
    |
    v
Download model artifacts into a shared volume
    |
    v
Storage initializer exits successfully
    |
    v
Model server container starts
    |
    v
Model server loads files from the shared location
    |
    v
Readiness probe succeeds

This means a Pod can exist without the inference server ever starting.

Check init-container state with:

bash
1kubectl get pods -n ml-production

Then inspect the Pod:

bash
1kubectl describe pod <pod-name> -n ml-production

Read the storage initializer logs:

bash
1kubectl logs <pod-name> \
2  -n ml-production \
3  -c storage-initializer

Typical failures include:

Missing credentials

AccessDenied
NoCredentialProviders
403 Forbidden

Check:

  • ServiceAccount configuration
  • IAM role or workload identity
  • Kubernetes Secrets
  • Bucket policy
  • Object path

Wrong model path

The bucket may exist while the model directory does not.

Check the full URI, including version folders and filenames.

Download is too slow

Large LLM weights can take many minutes to download. This creates long startup and scaling delays.

For very large models, repeatedly downloading weights during every cold start can become operationally expensive. Consider:

  • Persistent volumes
  • Node-local caches
  • Pre-pulled artifacts
  • OCI-packaged model artifacts
  • KServe modelcars or local model caching features
  • Keeping a minimum number of replicas warm

Insufficient ephemeral disk

Downloading a 40 GB model into a Pod with limited ephemeral storage will fail even when the GPU has enough memory.

Model-serving capacity planning must include:

  • Model artifact size
  • Temporary download space
  • Unpacked model size
  • Runtime cache usage
  • Container filesystem limits
  • Node disk availability

GPU capacity is only one part of the system.

4. A Standard Inference Protocol

One reason platforms use KServe is to avoid building a completely different client contract for every model framework.

KServe supports a standardized V2 inference protocol containing common APIs for areas such as:

  • Server health
  • Server metadata
  • Model metadata
  • Inference requests

A conceptual inference request may look like:

http
1POST /v2/models/fraud-detector/infer
2Content-Type: application/json
json
1{
2  "inputs": [
3    {
4      "name": "transactions",
5      "shape": [1, 4],
6      "datatype": "FP32",
7      "data": [125.5, 3.0, 0.91, 1.0]
8    }
9  ]
10}

The major platform benefit is not that every model has identical internal behavior. It is that the external serving contract becomes more predictable.

Your application team can build reusable logic for:

  • Authentication
  • Retries
  • Timeouts
  • Request tracing
  • Metrics
  • Load testing
  • Error handling

without learning a completely unrelated API for every model type.

However, do not confuse a standard transport with identical semantics.

A scikit-learn classifier, an image model, and an LLM still have different:

  • Input schemas
  • Output schemas
  • Latency characteristics
  • Resource requirements
  • Failure modes

Standardization reduces integration work. It does not remove the need to understand the model.

Autoscaling: The Diagram Is Correct, but Deployment Mode Matters

The diagram states:

No traffic means no GPU bill.

That can be true, but it is not universally true for every KServe configuration.

KServe supports different deployment modes, and autoscaling behavior depends heavily on which mode you use.

Standard Mode

Standard mode uses familiar Kubernetes primitives such as:

  • Deployment
  • Service
  • Gateway API or Ingress
  • Horizontal Pod Autoscaler
  • Optional KEDA integration

This mode is commonly preferred for production LLM serving because it gives direct control over:

  • GPU allocation
  • Pod lifecycle
  • Volumes
  • Long-running requests
  • Streaming
  • Scheduling
  • Predictable minimum capacity

Standard HPA-based scaling normally does not provide HTTP scale-from-zero by itself.

A typical Standard mode annotation is:

yaml
1metadata:
2  annotations:
3    serving.kserve.io/deploymentMode: Standard

Standard mode is a strong choice when:

  • Cold starts are too expensive
  • Models take a long time to load
  • Requests stream for a long duration
  • GPU scheduling must be predictable
  • You need normal Kubernetes operational behavior
  • You want to use custom metrics through an external autoscaler

Knative Mode

Knative mode is designed around request-driven serverless behavior.

It can provide:

  • Request- or concurrency-based scaling
  • Scale-down to zero
  • Scale-up from zero
  • Revision management
  • Traffic splitting between revisions
  • Canary rollouts

This is attractive for models with:

  • Intermittent traffic
  • Fast startup
  • Smaller artifacts
  • Shorter request duration
  • High sensitivity to idle infrastructure cost

A scale-to-zero configuration may include:

yaml
1spec:
2  predictor:
3    minReplicas: 0

But scale-to-zero has a cost: cold starts.

For an LLM, cold-start time can include:

Schedule GPU Pod
+ pull container image
+ download model weights
+ initialize CUDA
+ allocate GPU memory
+ load or map weights
+ warm runtime kernels
+ become ready

That may take seconds or several minutes.

Therefore, “scale to zero” is not automatically good architecture.

The correct question is:

Is the money saved during idle periods worth the additional latency and operational complexity during cold starts?

For a low-traffic internal classifier, probably yes.

For a customer-facing LLM API with a strict first-token SLO, probably not.

Canary Deployments: Splitting Traffic Between Model Versions

The diagram shows:

  • 90% traffic to predictor v1
  • 10% traffic to predictor v2
  • Promotion of v2 if it remains healthy

This is a canary rollout.

The idea is simple:

Production traffic
      |
      +---- 90% ----> Existing model revision
      |
      +---- 10% ----> Candidate model revision

A canary reduces blast radius.

Instead of replacing every request path at once, you expose the new version to a controlled percentage of traffic.

However, “the Pod is healthy” is not enough to promote a model.

For normal software, health may mean:

  • Process is running
  • Readiness probe passes
  • Error rate is acceptable
  • Latency is acceptable

For a model, you must also monitor behavior such as:

  • Prediction quality
  • Output distribution
  • Hallucination rate
  • Refusal behavior
  • Token usage
  • Cost per request
  • Safety-policy violations
  • Drift across important customer segments
  • Business conversion metrics

A technically healthy model can still be a bad model.

A responsible canary decision combines infrastructure metrics and model-quality metrics.

Example promotion gates:

HTTP 5xx rate < 0.5%
p95 latency increase < 10%
GPU OOM count = 0
Cost/request increase < 5%
Offline evaluation score does not regress
Online business metric does not regress
Safety violation rate does not increase

Canary traffic percentages alone do not make a deployment safe. Observability and rollback policy do.

End-to-End Request Flow

Let us walk through the complete path of one request.

Step 1: The client sends a request

Application -> model.example.com

The request includes:

  • Hostname
  • Path
  • Payload
  • Authentication information
  • Trace or request ID
  • Timeout expectations

Step 2: The gateway receives it

The gateway may handle:

  • TLS termination
  • Host-based routing
  • Path matching
  • Authentication integration
  • Rate limiting
  • Traffic splitting
  • Request size limits

Step 3: Traffic is routed to a model Service or revision

The route selects:

  • Stable revision
  • Canary revision
  • Predictor component
  • Transformer component, when configured

Step 4: Kubernetes sends the request to a ready Pod

The Service only routes to endpoints considered ready.

If there are zero ready endpoints, the client may see a 503.

Step 5: The model server processes the request

The runtime performs operations such as:

  • Input validation
  • Tokenization or preprocessing
  • Batching
  • GPU or CPU execution
  • Postprocessing
  • Response serialization

Step 6: Metrics and logs are emitted

A production system should capture:

  • Request count
  • Error count
  • Queue time
  • Inference time
  • End-to-end latency
  • Time to first token
  • Inter-token latency
  • Input and output token counts
  • GPU memory utilization
  • GPU compute utilization
  • Model-load duration
  • Autoscaling events
  • Revision name
  • Model version

Step 7: The response returns through the same network path

Any proxy in the path can influence:

  • Streaming
  • Buffering
  • Idle timeout
  • Maximum request duration
  • Maximum body size
  • Connection reuse

This is particularly important for LLM streaming. A model runtime may stream correctly while an ingress proxy buffers the entire response.

What Happens When You Apply an Update?

Suppose the original model points to:

yaml
1storageUri: s3://company-models/fraud-detector/v17

You change it to:

yaml
1storageUri: s3://company-models/fraud-detector/v18

Then run:

bash
1kubectl apply -f inference-service.yaml

The control-plane flow is approximately:

API server stores updated desired state
        |
        v
KServe controller sees generation change
        |
        v
Controller creates or updates serving resources
        |
        v
New Pod or revision starts
        |
        v
Storage initializer downloads v18
        |
        v
Runtime loads v18
        |
        v
Readiness checks pass
        |
        v
Traffic moves according to rollout configuration
        |
        v
InferenceService status is updated

The key word is eventually.

Kubernetes is an eventually consistent control system. The state transition takes time.

Your deployment automation should wait for readiness instead of assuming immediate success.

For example:

bash
1kubectl wait \
2  --for=condition=Ready \
3  inferenceservice/fraud-detector \
4  -n ml-production \
5  --timeout=10m

Then run a real inference smoke test.

A readiness condition verifies infrastructure state. A smoke test verifies the request path.

You need both.

The Correct Debugging Strategy

The biggest mistake is treating the entire platform as one black box.

Debug from the outside inward, one layer at a time.

Layer 1: Was the Custom Resource Accepted?

bash
1kubectl get inferenceservice -n ml-production
2kubectl describe inferenceservice fraud-detector -n ml-production

Look for:

  • Validation errors
  • Status conditions
  • Runtime selection errors
  • Reconciliation errors
  • A missing URL
  • Ready=False

Also inspect events:

bash
1kubectl get events \
2  -n ml-production \
3  --sort-by=.metadata.creationTimestamp

Layer 2: Is the KServe Controller Healthy?

bash
1kubectl get pods -n kserve

Controller logs:

bash
1kubectl logs \
2  -n kserve \
3  deployment/kserve-controller-manager \
4  --all-containers=true

Look for:

  • Reconciliation failures
  • Admission webhook errors
  • RBAC errors
  • Invalid generated resources
  • Runtime resolution failures

If the controller is unhealthy, editing the predictor Pod will not solve the root problem.

Layer 3: What Resources Were Generated?

Start by listing the namespace:

bash
1kubectl get all -n ml-production

Then inspect mode-specific resources:

bash
1kubectl get deployment -n ml-production
2kubectl get service -n ml-production
3kubectl get hpa -n ml-production
4kubectl get httproute -n ml-production
5kubectl get ingress -n ml-production

For Knative-based deployments:

bash
1kubectl get kservice -n ml-production
2kubectl get revision -n ml-production
3kubectl get configuration -n ml-production
4kubectl get route -n ml-production

You are trying to discover where the desired-state translation failed.

Layer 4: Did Kubernetes Schedule the Pod?

bash
1kubectl get pods -n ml-production -o wide

If the Pod is Pending, run:

bash
1kubectl describe pod <pod-name> -n ml-production

Typical scheduling failures:

  • No GPU nodes available
  • GPU resource name is incorrect
  • Node selector does not match
  • Taints are not tolerated
  • Insufficient memory
  • Insufficient ephemeral storage
  • Persistent volume cannot attach
  • Namespace quota exceeded

A Pending Pod has not reached your application code. Do not start debugging Python or vLLM yet.

Layer 5: Did Model Download Complete?

Check init containers:

bash
1kubectl get pod <pod-name> \
2  -n ml-production \
3  -o jsonpath='{.status.initContainerStatuses}'

Then:

bash
1kubectl logs <pod-name> \
2  -n ml-production \
3  -c storage-initializer

If this fails, focus on storage, credentials, networking, disk, and artifact layout.

Layer 6: Did the Model Runtime Start?

List containers:

bash
1kubectl get pod <pod-name> \
2  -n ml-production \
3  -o jsonpath='{.spec.containers[*].name}'

Then read the predictor container logs:

bash
1kubectl logs <pod-name> \
2  -n ml-production \
3  -c kserve-container

The exact container name can vary by runtime and configuration.

Look for:

  • CUDA initialization errors
  • GPU out-of-memory errors
  • Unsupported model architecture
  • Missing tokenizer files
  • Invalid model format
  • Runtime argument errors
  • Port-binding errors
  • Health server failures

Layer 7: Is the Pod Ready?

bash
1kubectl get pod <pod-name> -n ml-production
2kubectl describe pod <pod-name> -n ml-production

A running process is not necessarily ready.

Readiness may fail because:

  • The model is still loading
  • The health endpoint is incorrect
  • Startup takes longer than the probe allows
  • The probe uses the wrong port
  • The runtime is alive but cannot serve the model

For large models, probe configuration must account for realistic startup time.

Layer 8: Does the Service Have Endpoints?

bash
1kubectl get service -n ml-production
2kubectl get endpoints -n ml-production

No endpoints usually means no ready Pods match the Service selector.

Test from inside the cluster:

bash
1kubectl run debug-shell \
2  --rm -it \
3  --restart=Never \
4  --image=curlimages/curl \
5  -- sh

Inside that shell:

bash
1curl -v http://<service-name>.<namespace>.svc.cluster.local:<port>/health

If internal traffic works but public traffic fails, the problem is likely above the Service layer.

Layer 9: Does the External Route Work?

Check the route, hostname, certificates, and gateway status.

Example:

bash
1curl -v \
2  -H "Host: fraud-detector.example.com" \
3  http://<gateway-address>/v2/models/fraud-detector

For TLS:

bash
1curl -v https://fraud-detector.example.com/v2/models/fraud-detector

Pay attention to:

  • DNS resolution
  • Certificate name
  • Host header
  • Path rewriting
  • Proxy timeout
  • Response buffering
  • Authentication layer

Layer 10: Is the Model Correct?

Once infrastructure works, validate the model itself.

Run:

  • Known-input tests
  • Golden-dataset tests
  • Load tests
  • Regression tests
  • Safety tests
  • Cost tests
  • Streaming tests
  • Timeout and cancellation tests

A 200 OK only proves that the request completed. It does not prove that the answer is correct.

Common Production Mistakes

Mistake 1: Using latest for the runtime image

yaml
1image: company/model-server:latest

This destroys reproducibility.

Use immutable versions or digests:

yaml
1image: company/model-server@sha256:<digest>

Mistake 2: Using a mutable model path

yaml
1storageUri: s3://models/fraud-detector/current

If current changes in place, two Pods may start with different model contents while appearing to run the same version.

Prefer immutable paths:

yaml
1storageUri: s3://models/fraud-detector/2026-07-14-17/

Store the model checksum as deployment metadata.

Mistake 3: Setting no resource requests

Without realistic requests, Kubernetes cannot schedule intelligently.

For GPU inference, define:

  • CPU request
  • Memory request
  • GPU request
  • Ephemeral storage request where needed

The model server still needs CPU for tokenization, networking, serialization, and control work.

Mistake 4: Scaling only on CPU

CPU utilization may not represent inference pressure.

For LLM systems, more useful signals can include:

  • In-flight requests
  • Queue depth
  • Tokens generated per second
  • Time to first token
  • KV-cache utilization
  • GPU utilization
  • GPU memory utilization

An autoscaler should react to the bottleneck, not simply to the easiest metric to collect.

Mistake 5: Enabling scale-to-zero without measuring cold start

Measure these separately:

Pod scheduling time
Container image pull time
Model download time
Runtime initialization time
Model load time
Readiness delay
First-request latency

Then decide whether scale-to-zero satisfies the product SLO.

Mistake 6: Treating readiness as model-quality validation

Readiness probes validate service availability.

They do not validate:

  • Accuracy
  • Safety
  • Bias
  • Drift
  • Hallucinations
  • Business impact

Model promotion needs evaluation gates outside Kubernetes health checks.

Mistake 7: Logging complete inference payloads

Model requests may contain sensitive customer or business data.

Use:

  • Structured metadata
  • Request IDs
  • Redacted fields
  • Sampled payload logging
  • Explicit retention limits
  • Access controls

Observability should not become a data leak.

A More Production-Ready Example

The exact fields available depend on your KServe version and runtime, but a production manifest should make important choices explicit.

yaml
1apiVersion: serving.kserve.io/v1beta1
2kind: InferenceService
3metadata:
4  name: sentiment-classifier
5  namespace: ml-production
6  annotations:
7    serving.kserve.io/deploymentMode: Standard
8spec:
9  predictor:
10    minReplicas: 2
11    maxReplicas: 10
12    model:
13      modelFormat:
14        name: sklearn
15      protocolVersion: v2
16      storageUri: s3://company-models/sentiment/2026-07-14-01/
17      resources:
18        requests:
19          cpu: "1"
20          memory: "2Gi"
21          ephemeral-storage: "4Gi"
22        limits:
23          cpu: "4"
24          memory: "8Gi"
25          ephemeral-storage: "10Gi"

This is better because it communicates:

  • Deployment mode
  • Minimum availability
  • Maximum scale
  • Immutable model location
  • Standard protocol
  • Resource planning
  • Ephemeral disk requirements

A manifest should describe operational intent, not only the minimum syntax required to start a container.

Observability You Should Have Before Production

At minimum, build dashboards for four layers.

Kubernetes layer

  • Pod count
  • Pod restarts
  • Pending Pods
  • Scheduling latency
  • CPU and memory
  • Ephemeral storage
  • Node pressure
  • HPA or KEDA decisions

GPU layer

  • GPU utilization
  • GPU memory
  • Power usage
  • Temperature
  • OOM events
  • Hardware errors
  • Per-Pod GPU allocation

Runtime layer

  • Requests per second
  • Queue depth
  • Batch size
  • Model-load time
  • Inference duration
  • Time to first token
  • Tokens per second
  • Active sequences
  • KV-cache pressure

Product layer

  • Success rate
  • User-visible latency
  • Cost per request
  • Output quality
  • Safety failures
  • Model-version distribution
  • Canary-vs-stable comparison

A platform is observable only when you can connect one customer request across all four layers.

Use a request ID or trace ID that survives:

Gateway
-> KServe route
-> Predictor
-> Runtime
-> Application metrics

When KServe Is a Good Fit

KServe is useful when you need a shared platform across multiple teams or model types.

It is especially valuable when you need:

  • Kubernetes-native deployment
  • Repeatable model-serving conventions
  • Multiple serving runtimes
  • Standardized inference APIs
  • Controlled rollouts
  • Autoscaling integration
  • Central platform governance
  • Consistent networking and observability
  • Self-service deployment for ML teams

It reduces the amount of custom infrastructure every model team must build.

When KServe May Be Too Much

KServe is not automatically the correct answer for every model.

It may be unnecessary when:

  • You have one small internal model
  • A normal Deployment and Service are sufficient
  • Your team does not operate Kubernetes well
  • You do not need multiple runtimes or platform standardization
  • The additional control plane creates more complexity than value
  • A managed inference product already meets your requirements

Abstraction is valuable when it removes repeated complexity.

It is harmful when the team adopts it without understanding the underlying system.

The Senior Engineer’s Mental Checklist

When an InferenceService is unhealthy, ask these questions in order:

  1. Did Kubernetes accept the custom resource?
  2. Did the KServe controller reconcile it?
  3. Which lower-level resources were generated?
  4. Was the Pod scheduled?
  5. Did the storage initializer download the model?
  6. Did the runtime load the model?
  7. Did readiness pass?
  8. Does the Service have endpoints?
  9. Does internal cluster routing work?
  10. Does external gateway routing work?
  11. Does the inference protocol request match the runtime?
  12. Is the model output correct?
  13. Is autoscaling reacting to the right signal?
  14. Is the canary healthy technically and behaviorally?
  15. Can the deployment be rolled back quickly?

This sequence prevents random debugging.

Final Takeaway

The most important lesson from the diagram is not the YAML syntax.

It is this:

KServe gives you a higher-level API, but the system underneath is still Kubernetes.

The InferenceService is your desired state.

The KServe controller translates that desired state into a serving stack.

The serving stack still contains familiar infrastructure:

  • Workloads
  • Pods
  • Services
  • Gateways
  • Init containers
  • Autoscalers
  • Revisions
  • Runtime processes

KServe makes the common path easier. It does not eliminate the need to understand networking, scheduling, storage, scaling, health checks, or model runtime behavior.

Use the abstraction when deploying.

Break the abstraction when debugging.

That is how a senior engineer operates a platform instead of merely using its YAML.

Bhupesh Kumar

Bhupesh Kumar

Backend engineer building scalable APIs and distributed systems with Node.js, TypeScript, and Go.