Deploying an open-weight model on Kubernetes in 2026 is, depending on who you ask, either a solved problem or five unsolved problems wearing a trench coat. The serving half really is close to solved: vLLM or SGLang serves the weights, DRA binds the GPU to the pod, LeaderWorkerSet handles multi-node topologies.
But every one of those answers lives inside a single cluster, and every one of them is hand-wired per GPU type. Ask for an 8B model on whatever card has room and you get a pile of YAML that names the machine, the vendor plugin, and the autoscaler — separately, by hand, per cloud.
Here is the verdict up front: the valuable half of Crossplane's Modelplane project is not its multi-cloud fleet scheduler, which a single-fleet self-hosted PaaS should skip. It is the claim-based API shape — request "an inference endpoint with at least 20Gi of VRAM" instead of hand-wiring node affinity, device plugins, and autoscaling per GPU vendor. The before/after below is the whole argument; the rest of this post is the evidence.
| Concern | The hand-wired stack (today) | The claim (Modelplane's shape) |
|---|---|---|
| Which hardware | nodeAffinity pinning a node label you maintain | CEL device selector: VRAM ≥ 20Gi, scheduler figures out the rest |
| GPU allocation | Opaque integer nvidia.com/gpu: 1 — a 4060 and an H100 both equal "1" | Attribute request: model, memory, NVLink topology |
| Device drivers | Per-cloud plugin install (EKS needs the NVIDIA plugin by hand; GKE ships it) | Platform's problem, declared once per hardware tier |
| Serving stack | Hand-rolled vLLM Deployment + Service + HPA per model | ModelDeployment: replicas, engine, template in one object |
| Scaling | Per-vendor autoscaler wiring per cluster | Fleet-level scheduling + gateway routing across replicas |
| Multi-cloud sprawl | N/A (you have one fleet) | GKE/EKS/Nebius/Crusoe provider matrix — skip this row |
The short version: borrow the claim, skip the cloud sprawl. A Cluster-API fleet on owned hardware wants Modelplane's API ergonomics grafted onto one hardware pool, not its multi-cloud control plane.
What Modelplane actually is (and isn't)
Modelplane (modelplaneai/modelplane) is an open-source, fleet-level inference control plane built on Crossplane, described in an accepted May 2026 design doc by Nic Cope. It manages GPU clusters across clouds and regions, schedules model deployments across the fleet, and routes inference traffic through a unified gateway. An ML team deploys Qwen3-8B like this — this is close to the doc's own example, vLLM v0.23.0 and all:
apiVersion: modelplane.ai/v1alpha1
kind: ModelDeployment
metadata:
name: qwen3-8b
namespace: ml-team
spec:
replicas: 1
clusterSelector:
matchLabels:
modelplane.ai/region: us
engines:
- name: qwen3-8b
members:
- role: Standalone
nodeSelector:
devices:
- name: gpu
count: 1
selectors:
- cel: |
device.capacity["gpu.nvidia.com"].memory.compareTo(quantity("20Gi")) >= 0
template:
spec:
containers:
- name: engine
image: vllm/vllm-openai:v0.23.0
args:
- "--model=Qwen/Qwen3-8B"
- "--gpu-memory-utilization=0.92"Each ModelDeployment becomes one or more ModelReplicas — each a complete serving instance on an InferenceCluster — and a ModelService routes traffic across replicas and optionally external endpoints. The hierarchy deliberately mirrors Kubernetes core: ModelDeployment → ModelReplica → ModelService → ModelEndpoint parallels Deployment → Pod → Service → Endpoint.
Underneath, Crossplane compositions (written as Python functions) provision whole clusters — VPC, subnets, EKS cluster with system and GPU node groups, plus the NVIDIA device plugin EKS doesn't ship by default. Newer work moves device selection to a DRA-style spec.devices[] shape, while provider coverage expands across GPU clouds: Nebius landed, and Crusoe, Fluidstack, and Voltage Park are queued as issues.
What it isn't: a single-cluster serving story. vLLM, KServe, and llm-d already own that layer. Modelplane sits above them — fleet scheduling, fleet capacity, fleet routing — which is exactly why a single-fleet PaaS should read it as a parts catalog, not a dependency.
The idea worth stealing: a stable claim API over heterogeneous hardware
Crossplane's core pattern fits in three sentences. A platform author defines the API with a CompositeResourceDefinition (XRD) — "this is what an inference endpoint looks like." A tenant requests one with a namespaced claim — "I want that, this big, in this region." A composition function renders the claim into whatever managed resources actually satisfy it — clusters, node groups, deployments, gateways. The tenant's YAML never names a cloud, a driver, or a machine type.
Three things make this pattern land harder for GPUs than it ever did for databases or buckets:
- Integer device counts lie.
nvidia.com/gpu: 1treats an RTX 4000 and an H100 as the same unit. Modelplane's CEL selector asks for what the workload actually needs — 20Gi of VRAM — and lets the scheduler match capability to hardware. Kubernetes' own Dynamic Resource Allocation, GA in the 1.34–1.35 window with NVIDIA's driver now donated into kubernetes-sigs, is the same insight becoming core API: describe the device you need, don't count opaque units. - The vendor matrix never stops moving. New accelerators, new GPU clouds, new MIG/partitioning schemes arrive quarterly. A claim API absorbs that churn behind a stable surface; hand-wired YAML re-learns it per vendor, per cluster. Modelplane's provider queue is the proof — every new GPU cloud is a provider to write, and claim authors never notice.
- Inference has a routing half that training doesn't. A model isn't placed once; its traffic is balanced across replicas, regions, and providers continuously. Folding scheduling and gateway routing into the same object that provisions the hardware is what makes the claim "an inference endpoint" rather than "a GPU VM with extra steps."
Borrow vs skip: a Hetzner CAPI fleet's decision table
Graft this onto a Cluster-API fleet with one hardware pool — say, Hetzner GPU boxes — and the verdicts split cleanly:
| Layer | Verdict | Why |
|---|---|---|
| Claim-shaped GPU API ("give me an endpoint for this model") | Borrow | This is the durable idea; it survives every hardware refresh |
| InferenceClass-style hardware tiers | Borrow | Name your tiers (e.g. a 20GB tier, an 80GB tier) instead of leaking machine types to tenants |
| DRA-native attribute device selection | Borrow | GA core API, NVIDIA driver in kubernetes-sigs — the integer-count era is ending anyway |
| Gateway routing across replicas | Borrow | Every multi-replica model needs it; don't re-solve traffic splitting per tenant |
| Multi-cloud provider matrix (GKE/EKS/Nebius/Crusoe/…) | Skip | Solves a problem a single-fleet Hetzner operator doesn't have — and bex explicitly isn't a multi-cloud abstraction layer |
| Full fleet scheduler + global gateway | Skip (for now) | Fleet scheduling across one region's identical boxes is a simpler bin-pack; adopt when the second hardware pool actually exists |
The honest middle: you don't need Crossplane itself to borrow the shape. A small CRD (or even a validated bex API field) offering "model + hardware tier + replicas → endpoint URL" captures ~80% of the value with none of the multi-cloud machinery. Adopt the grammar, not the implementation.
The cost anchor that decides it: one €184/mo card, one tenant bill
Concretize with real hardware. Hetzner's GEX44 — RTX 4000 SFF Ada, 20GB VRAM — runs €184/month plus a €79 setup fee. A quantized Qwen3-8B at 16K context fits comfortably in that 20GB. So one tenant's 8B model on whole-GPU allocation consumes one full €184/mo card — while leaving headroom on the card that whole-GPU allocation can't resell to anyone else.
Sensitivity check, because this is where the claim API earns or loses its keep:
- One tenant per card: €184/mo per model endpoint. Simple, defensible, wasteful — the card's spare capacity idles.
- Two tenants per card (fractional sharing): ~€92/mo per endpoint. Same hardware, double the revenue per card — but only if the platform can partition and isolate the device, which is exactly what HAMi-style vGPU sharing and DRA partitioning exist for.
That ordering matters: fractional sharing is the dependency before the claim API, not after. A beautiful declarative endpoint API over whole-GPU allocation just lets tenants waste €184/mo cards more ergonomically. The platform work that actually moves the unit economics — DRA drivers on your node image, a sharing/isolation story per card, per-tenant usage accounting — has to land first; the claim is the interface that makes that machinery self-service afterward.
The takeaway for a git-push PaaS
Modelplane validates a thesis, not a dependency: inference hardware wants a stable, declarative, claim-shaped API because the hardware underneath will not sit still. For a self-hosted, single-fleet PaaS, the correct response is to steal the grammar — capability selectors, hardware tiers, endpoint claims, gateway routing — and skip the multi-cloud fleet control plane that exists to serve GPU clouds you'll never touch. Build the sharing layer first (that's where the €184/mo math moves), then put the claim API on top so tenants can spend it without learning your machine inventory.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



