Use this page to deploy the Cosmos3 Certified NIM on Kubernetes with the shared
nim-wfm Helm chart. Model selection, hardware floors, and verification are
the same as in Deployment.
- A Kubernetes cluster with GPU nodes that match a row in the Support matrix, with the NVIDIA GPU Operator installed.
- Helm and
kubectlaccess to the target namespace. The commands below use the default namespace. - A storage class for the persistent model cache.
NGC_API_KEYexported as described in Network and NGC access.
The chart needs an image pull secret and a generic secret with a key named
NGC_API_KEY:
kubectl create secret docker-registry ngc-secret \
--docker-server=nvcr.io \
--docker-username='$oauthtoken' \
--docker-password="$NGC_API_KEY"
kubectl create secret generic ngc-api \
--from-literal=NGC_API_KEY="$NGC_API_KEY"This page uses nim-wfm chart version 1.3.0:
export NIM_WFM_VERSION='1.3.0'
helm fetch "https://helm.ngc.nvidia.com/nim/charts/nim-wfm-${NIM_WFM_VERSION}.tgz" \
--username='$oauthtoken' \
--password="$NGC_API_KEY"The chart README lists every value supported by that version:
helm show readme "nim-wfm-${NIM_WFM_VERSION}.tgz"
helm show values "nim-wfm-${NIM_WFM_VERSION}.tgz"Save the following as cosmos3-values.yaml. It deploys the Nano Generator,
optimized for latency, on one GPU:
image:
repository: nvcr.io/nim/nvidia/cosmos3
tag: "2.0.0"
imagePullSecrets:
- name: ngc-secret
model:
ngcAPISecret: ngc-api
nimCache: /opt/nim/.cache
persistence:
enabled: true
size: 220Gi
sharedMemory:
sizeLimit: 16Gi
resources:
limits:
nvidia.com/gpu: 1
env:
- name: NIM_MODEL_TYPE
value: generator
- name: NIM_MODEL_VARIANT
value: nano
- name: NIM_PERF_PROFILE
value: latencyAdjust it for the chosen configuration:
- Model and GPUs: Change
NIM_MODEL_VARIANT,NIM_PERF_PROFILE, andnvidia.com/gputogether using the Generator configurations. The GPU count does not select a GPU type; usenodeSelectororaffinityto target eligible GPU nodes, andtolerationsif those nodes are tainted. On nodes with MIG enabled, request the MIG resource instead ofnvidia.com/gpu, for examplenvidia.com/mig-4g.24gb: 2. - Cache size:
220Giis a starting size for large model files. Setpersistence.storageClassif the cluster has no default storage class. - Memory: A pod memory limit counts as system memory during profile selection. Keep any limit at or above the host RAM value of the selected row.
- Shared memory: The chart mounts a memory-backed
emptyDirat/dev/shmby default.sharedMemory.sizeLimit: 16Gimatches the 16 GiB that the Docker launch allocates. - Pod user: The chart runs the pod as UID and GID 1000 with
fsGroup: 1000(podSecurityContext). The cache volume must be writable by that user; storage that ignoresfsGroup, such as some NFS exports andhostPathdirectories, needs matching ownership.
Set these through chart values rather than env:
| Chart value | Replaces |
|---|---|
model.nimCache |
NIM_CACHE_PATH |
model.ngcAPISecret |
NGC_API_KEY |
model.apiPort |
NIM_HTTP_API_PORT |
model.jsonLogging |
NIM_LOGGING_JSONL |
model.logLevel |
NIM_LOG_LEVEL |
Add other Configuration variables to env.
To check profile compatibility before a cold download, run the Docker pre-download profile preflight on a host with the same GPUs and selectors.
For Reasoner, replace env with the Reasoner selectors and choose the GPU
count from the Reasoner configurations.
Reasoner does not use NIM_PERF_PROFILE:
env:
- name: NIM_MODEL_TYPE
value: reasoner
- name: NIM_MODEL_VARIANT
value: nanoOn DGX Spark/GB10 or Jetson AGX Thor nodes, also set
NIM_GPU_MEMORY_UTILIZATION to "0.80" for image-only workloads or "0.70"
for video or mixed-media workloads; see
Set the Reasoner memory share.
To export traces and metrics over OTLP, add these entries to env. The
endpoint assumes an OpenTelemetry Collector DaemonSet that listens on host
port 4318; change OTEL_EXPORTER_OTLP_ENDPOINT for other collector layouts:
- name: NIM_ENABLE_OTEL
value: "1"
- name: OTEL_SERVICE_NAME
value: cosmos3
- name: OTEL_TRACES_EXPORTER
value: otlp
- name: OTEL_METRICS_EXPORTER
value: otlp
- name: HOST_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://$(HOST_IP):4318"HOST_IP must come before OTEL_EXPORTER_OTLP_ENDPOINT so that Kubernetes
can expand it.
The chart README documents these optional features; all are off by default:
- Prometheus metrics: Set
metrics.serviceMonitor.enabled: trueto create a Prometheus OperatorServiceMonitorthat scrapes/v1/metricson thehttp-apiport. See Metrics. - Ingress:
ingress.enabled,ingress.className,ingress.hosts, andingress.tls. The NIM does not authenticate requests, so protect any ingress outside the cluster. - Autoscaling:
autoscaling.enabledwithminReplicas,maxReplicas, andmetrics. CPU and memory metrics are of limited use for scaling a NIM; use custom metrics, for example throughprometheus-adapter. Each new replica must download or mount the model before it becomes ready; see Storage and scaling.
Review the rendered manifests, then install the release:
helm template cosmos3 "nim-wfm-${NIM_WFM_VERSION}.tgz" -f cosmos3-values.yaml
helm install cosmos3 "nim-wfm-${NIM_WFM_VERSION}.tgz" -f cosmos3-values.yaml
kubectl get pods -wCold download, materialization, and model load can take much longer than pod
startup. Send requests only after the pod reports Ready. Kubernetes restarts
the container if it is not ready within startupProbe.initialDelaySeconds plus
startupProbe.failureThreshold × startupProbe.periodSeconds; raise
failureThreshold for slow first downloads.
The chart creates a ClusterIP Service with no ingress, and the NIM does not
handle authentication. Port-forward the Service for testing:
kubectl port-forward service/cosmos3-nim-wfm 8000:http-apiIn another terminal:
export NIM_URL=http://localhost:8000
curl -fsS "$NIM_URL/v1/health/ready"
curl -fsS "$NIM_URL/v1/metadata" | python3 -m json.toolConfirm that metadata reports generator and /v1/infer, or reasoner and
/v1/chat/completions. Then run a first request from
Use an existing NIM endpoint.
The chart supports these cache options; use only one:
| Option | Values | Notes |
|---|---|---|
| StatefulSet volume claims | persistence.enabled: true with the default statefulSet.enabled: true |
Default. Each replica gets its own claim and downloads the model before it becomes ready |
| Shared volume claim | persistence.enabled: true, statefulSet.enabled: false, persistence.accessMode: ReadWriteMany |
One cache shared by all replicas; needs a ReadWriteMany storage class |
| Existing claim | persistence.existingClaim: <claim-name> |
Reuse a pre-filled cache. Run one replica unless the claim is ReadWriteMany |
| Direct NFS | nfs.enabled: true, nfs.server, nfs.path |
Mount options cannot be set per pod; an NFS-backed claim is usually a better choice |
hostPath |
hostPath.enabled: true, hostPath.path |
Ties pods to one node and has security implications |
Set replicaCount for a fixed number of replicas. With the default
StatefulSet, you can pre-populate caches by scaling up to the maximum replica
count once and then scaling down; the retained claims let later scale-ups
start without a new download.
With statefulSet.enabled: false and a ReadWriteOnce volume, scaling or
rolling upgrades beyond one pod are likely to fail. Use a ReadWriteMany
storage class, manually cloned ReadOnlyMany volumes of a pre-filled cache,
or direct NFS. Without persistence, the cache is an emptyDir and every pod
start downloads the model again.
| Symptom | Check |
|---|---|
Pod stays Pending |
Run kubectl describe pod <pod> and check Events for insufficient GPUs, untolerated taints, or an unbound volume claim |
| Pod restarts while preparing the model workspace | The startup probe ran out before the download finished; increase startupProbe.failureThreshold |
Scaling or upgrade fails with statefulSet.enabled: false |
The cache volume is ReadWriteOnce; see Storage and scaling |
| Profile selection fails | Compare GPU type, count, free memory, and pod memory limit with the Support matrix; see Troubleshooting |
helm uninstall cosmos3
kubectl get pvcStatefulSet volume claims are retained by default and keep the model cache.
Delete them and the ngc-secret and ngc-api secrets only when they are no
longer needed.