Modelplane Modelplane docs

Laguna-S-2.1

Poolside’s Laguna-S-2.1 (118B total, 8B active MoE) served FP8 as a single Standalone vLLM engine on one 8x H100 node on Nebius. The FP8 weights (~121 GiB) fit one node with headroom for KV cache, so the engine is tensor-parallel across the 8 GPUs over NVLink, with no gang and no prefill/decode disaggregation. Weights stage once to a ModelCache on a Nebius shared filesystem and mount at /mnt/models.

This recipe was run end to end on Nebius (eu-north): serving and tool calling validated on a single 8x H100 node. poolside/Laguna-S-2.1-FP8 is a public repository, so no Hugging Face token or Secret is needed. Apply the platform side first, then the ML side.

Platform

inference-class-nebius.yaml
# An InferenceClass describing a Nebius gpu-h100-sxm node with 8x NVIDIA
# H100 80GB. Nebius sizes nodes by platform + preset rather than an
# instance type; gpu-h100-sxm + 8gpu-128vcpu-1600gb is one 8x H100 SXM
# node. The devices block describes the hardware DRA-style; it is what
# the Laguna ModelDeployment's nodeSelector matches on.
apiVersion: modelplane.ai/v1alpha1
kind: InferenceClass
metadata:
  name: nebius-h100-8x
spec:
  description: "Nebius gpu-h100-sxm, 8x NVIDIA H100 80GB"
  provisioning:
    provider: Nebius
    nebius:
      platform: gpu-h100-sxm
      preset: 8gpu-128vcpu-1600gb
      diskSizeGb: 200
      driversPreset: cuda13.0
      accelerator:
        type: nvidia-h100
        count: 8
  devices:
  - name: gpu
    claim: DRA
    driver: gpu.nvidia.com
    deviceClassName: gpu.nvidia.com
    count: 8
    attributes:
      architecture: { string: Hopper }
      cudaComputeCapability: { version: "9.0.0" }
    capacity:
      # H100 80GB real usable VRAM (NVIDIA DRA driver), not nominal 80GB.
      memory: { value: "81559Mi" }
inference-cluster-nebius.yaml
# An InferenceCluster backed by a Nebius mk8s cluster with a single 8x
# H100 GPU node group. Modelplane provisions the full mk8s cluster (VPC,
# control plane, system + GPU node groups) and installs the inference
# stack. ModelCache RWX storage is auto-provisioned via a Nebius shared
# filesystem, so model-cache.yaml works unchanged.
#
# Auth is the Nebius ClusterProviderConfig named default (service-account
# key + projectID); Modelplane reuses that identity to reach the cluster.
#
# eu-north1 (Finland) keeps inference in the EU for data residency.
#
# Clean teardown - delete the ML resources first (they hold a usage on the
# cluster), then the cluster:
#   kubectl delete modeldeployment,modelservice,modelcache laguna -n ml-team
#   kubectl delete inferencecluster nebius-eu-north --cascade=foreground
apiVersion: modelplane.ai/v1alpha1
kind: InferenceCluster
metadata:
  name: nebius-eu-north
  labels:
    modelplane.ai/region: eu-north
spec:
  cluster:
    source: Nebius
    nebius: {}

  nodePools:
  - name: gpu-h100
    className: nebius-h100-8x
    nodeCount: 1
    minNodeCount: 1
    maxNodeCount: 1

Deployment

model-cache.yaml
# The model weights, staged once per cluster on a Nebius shared filesystem
# (RWX) and mounted at /mnt/models in the serving pod, so the engine reads
# FP8 weights (~121 GiB) locally instead of pulling them from Hugging Face
# on every start.
#
# poolside/Laguna-S-2.1-FP8 is a public repo (OpenMDW-1.1), so no authSecret /
# HF token is needed. Add one only if you point this at a gated repo.
apiVersion: modelplane.ai/v1alpha1
kind: ModelCache
metadata:
  name: laguna
  namespace: ml-team
spec:
  source: HuggingFace
  huggingFace:
    repo: poolside/Laguna-S-2.1-FP8
    sizeGiB: 200
model-deployment.yaml
# Laguna-S-2.1 (118B total / 8B active MoE) served FP8 as a single
# Standalone vLLM engine on one 8x H100 node, weights streamed from the
# shared ModelCache at /mnt/models. FP8 weights (~121 GiB) fit one node
# with headroom for KV cache, so no gang / multi-node and no
# prefill/decode disaggregation are needed - just tensor parallelism
# across the 8 GPUs over NVLink.
#
# Notes on the engine flags:
#   --tensor-parallel-size=8 shards the model across the 8 H100s in the
#     node. FP8 is selected by the checkpoint (Laguna-S-2.1-FP8), not a flag.
#   --tool-call-parser=poolside_v1 and --reasoning-parser=poolside_v1 are
#     Poolside-specific and must exist in the engine build; --trust-remote-code
#     loads Laguna's custom modeling code from the cached repo.
#   --enable-auto-tool-choice turns on server-side tool selection.
#   --max-model-len can be lowered to shrink the KV cache footprint; 262144
#     matches Poolside's reference config.
#   The engine container must be named `engine` and listen on :8000 (Modelplane
#     scrapes 8000).
#
# H100-specific FP8 settings, carried over from a community 4x H100 build:
#   VLLM_BLOCKSCALE_FP8_GEMM_FLASHINFER=0 selects an FP8 GEMM path that is
#     correct on Hopper; --enforce-eager avoids a CUDA-graph capture OOM on
#     H100 80GB. Validate and relax these during the run if profiling allows.
apiVersion: modelplane.ai/v1alpha1
kind: ModelDeployment
metadata:
  name: laguna
  namespace: ml-team
spec:
  replicas: 1
  template:
    spec:
      modelCacheRef:
        name: laguna
      engines:
      - name: laguna
        members:
        - role: Standalone
          nodeSelector:
            devices:
            - name: gpu
              count: 8
              selectors:
              - cel: |
                  device.driver == "gpu.nvidia.com" && device.capacity["gpu.nvidia.com"].memory.compareTo(quantity("79Gi")) >= 0
          template:
            spec:
              containers:
              - name: engine
                image: vllm/vllm-openai:v0.25.1
                command: ["vllm", "serve", "/mnt/models"]
                args:
                - --served-model-name=laguna
                - --tensor-parallel-size=8
                - --max-model-len=262144
                - --gpu-memory-utilization=0.9
                - --trust-remote-code
                - --enable-auto-tool-choice
                - --tool-call-parser=poolside_v1
                - --reasoning-parser=poolside_v1
                - --enforce-eager
                - --port=8000
                env:
                - name: VLLM_BLOCKSCALE_FP8_GEMM_FLASHINFER
                  value: "0"
model-service.yaml
# One OpenAI-compatible endpoint for the deployment. Read its public address:
#   kubectl get ms laguna -n ml-team -o jsonpath='{.status.address}'
# then call it, e.g.:
#   curl "$ADDR/v1/chat/completions" -H 'Content-Type: application/json' \
#     -d '{"model":"laguna","messages":[{"role":"user","content":"hello"}]}'
apiVersion: modelplane.ai/v1alpha1
kind: ModelService
metadata:
  name: laguna
  namespace: ml-team
spec:
  endpoints:
  - selector:
      matchLabels:
        modelplane.ai/deployment: laguna