VSS Warehouse — Helm Deploy
Do not use this skill for:
- Docker Compose warehouse deployment — use
vss-build-vision-ai'sreferences/warehouse.md; it owns theHARDWARE_PROFILE→ GPU mapping table and theblueprint_config.ymlstream-cap semantics this skill reuses. - Non-warehouse Helm profiles (
base,search,lvs,alerts) — those don't have abp-configuratorGPU-aware stream cap; deploy them per their own chart READMEs. - Runtime operations (adding cameras, querying behavior analytics) — use
vss-manage-alerts/vss-query-analyticsagainst the running deployment.
Why this exists
Docker Compose's warehouse deploy caps NUM_STREAMS per GPU automatically: the configurator reads
deploy/docker/industry-profiles/warehouse-operations/blueprint-configurator/blueprint_config.yml's
max_streams_supported table for the detected HARDWARE_PROFILE and mode, and clamps
final_stream_count = min(NUM_STREAMS, max_streams_supported).
The Helm charts (deploy/helm/industry-profiles/warehouse-operations/warehouse-{2d,3d,mv3dt}-app)
do not do this — their bp-configurator.env ships a fixed NUM_STREAMS and never sets
HARDWARE_PROFILE at all (ENABLE_PROFILE_CONFIGURATOR=false). A user who asks for more streams
than the GPU can sustain gets no protection. This skill closes that gap by computing the same cap
Compose would apply and writing it into a Helm values-override file before install.
Available Scripts
| Script | Purpose | Arguments |
|---|---|---|
../../../deploy/helm/industry-profiles/warehouse-operations/scripts/compute_stream_cap.py | Detect GPU (or take an explicit HARDWARE_PROFILE), read max_streams_supported from blueprint_config.yml, cap the requested stream count, and write a bp-configurator.env-patched values-override YAML. Pass any values file(s) your install already uses via -f so custom bp-configurator.env entries in them aren't dropped. | --mode {2d,3d,mv3dt} --num-streams N [--hardware-profile P] [--gpu-index I] [-f VALUES]... [-o FILE] |
This script has no skill/agent dependency — a user who doesn't want to use this skill can run it
directly (python3 compute_stream_cap.py --mode 2d --num-streams 8) and pass the generated file to
helm upgrade/install -f themselves.
Instructions
-
Precheck the cluster and required inputs before touching Helm — don't assume a fresh cluster already has these. Run each check and report pass/fail back to the user:
bashkubectl cluster-info # cluster reachable kubectl get nodes # all nodes Ready kubectl get storageclass # a StorageClass exists kubectl get nodes -o jsonpath='{.items[*].status.allocatable.nvidia\.com/gpu}{"\n"}' # non-empty -> GPU Operator has registered GPUs helm version --short # Helm 3.xAlso ask whether the user already has an NGC API key — that can't be checked from cluster state, only asked about.
On any failure, don't just link the user to the README and stop — hand them the actual fix, copied from the chart README, and offer to run it for them:
- No
StorageClass→ relay thelocal-path-provisionerinstall +kubectl patch storageclasssnippet fromwarehouse-<mode>-app/README.md§Prerequisites (bare-metal option) — or ask what StorageClass they intend to use if they already have one in mind. Multi-node cluster:local-path's node affinity can strandvss-vios-nvstreamer's PVCs across different nodes (didn't match PersistentVolume's node affinity) — relay the same section'snfs-subdir-external-provisionersnippet instead, and setvios.vstStorage.vstData,.vstVideo, and.streamerVideos.storageClasstonfs-clientvia three separate--setflags (or justglobal.storageClass) rather thanlocal-path. - No
nvidia.com/gpuallocatable → relay the NVIDIA GPU Operator install steps from §Prerequisites (links to the GPU Operator getting-started guide) and the recommended driver versions listed there. - Cluster unreachable / nodes not
Ready→ this one the user has to fix outside Helm/this skill entirely; say so plainly rather than suggesting a chart-level fix. - No NGC API key → point at §Required secrets in the chart README for how to create the pull secret, don't just say "get an NGC API key."
Only proceed to step 2 once cluster/StorageClass/GPU-Operator/Helm all pass and the user has confirmed they have an NGC API key — an install started before that will fail partway through in a way that's harder to debug than catching it here.
- No
-
Ask ingress vs. NodePort — this determines both what's installed in this step and which install command gets used in step 6, so resolve it before going further, don't default silently to one or the other:
- Ingress (needed off-cluster / for a stable hostname) → check whether an ingress
controller is already installed (
kubectl get ingressclass). If not, relay thehaproxy-ingressinstall snippet fromwarehouse-<mode>-app/README.md§"Install the ingress controller" and offer to run it. Note this is a one-time, per-cluster step, not per-app. - NodePort (simplest for a quick local/single-node deploy, no ingress controller needed) →
tell the user the chart ships
values-nodeport.yamlfor this — the install command in step 6 changes to-f values-nodeport.yamllayered under the stream-cap file, and the service URLs move to<NODE_IP>:<port>instead of<NODE_IP>/<path>. See §"No ingress controller: NodePort" and §URLs in the chart README for the exact ports. If the user hasn't said which they want and there's no clear signal (e.g. "just get it running locally" implies NodePort; "expose it for the team" implies Ingress), ask rather than guessing.
- Ingress (needed off-cluster / for a stable hostname) → check whether an ingress
controller is already installed (
-
Determine mode and whether to enable Alerts:
- Mode. Use
2d,3d, ormv3dtif the request already names one. Otherwise ask — don't guess:2d— 2D object detection & tracking.3d— standalone RTVI-CV-3D / multi-camera 3D tracking on calibrated inputs.mv3dt— Multi-View 3D Tracking warehouse profile. Also needsrtvi.vss-rtvi-cv.standaloneWarehouse.mv3dt.fusion.maxExpectedSensorsset to the effective stream count in step 6/7 (default4) — it's BEV fusion's own camera-count setting, separate fromNUM_STREAMS/syncFileCount, and the stream-cap script doesn't touch it.
- Alerts. Not a fourth mode — an optional overlay, off by default, and only available on
2d(warehouse-2d-appis the only chart withvss-alert-bridge/agent/vss-agent-uias dependencies;3dandmv3dtdon't have them). If the user is on3d/mv3dtand asks for Alerts, say it's not available there instead of trying to enable it. On2d, ask the user whether they want it, and explain the tradeoff first rather than enabling or skipping it for them: without Alerts they get the raw RT-CV detection/tracking stream; with it, detections also pass through a behavior-analytics stage and a VLM verification step (RT-VLM) before anything is surfaced as an incident, queryable through the agent/agent UI. That verification step is the reason to turn it on — it's what keeps every raw detection from becoming a ticket. If they want it, note the four flags have to be set together (vss-alert-bridge.enabled,agent.enabled,vss-agent-ui.enabled,rtvi.vss-rtvi-vlm.enabled— swap the last for an externalvlmBaseUrlif not using the in-cluster VLM) plus Kafka/Elasticsearch/VST endpoint values. Full block:warehouse-2d-app/README.md§Alerts — layer it in during step 6. - Stream count. Ask if not given; it sizes the
NUM_STREAMScap in step 5.
- Mode. Use
-
Ask whether the install customizes
bp-configurator.env(extra env vars, different defaults) — don't assume none exist just because the user didn't mention one. If they're unsure, ask them to check their existinghelm upgrade --installcommand for anything touchingbp-configurator.env, file-based or inline. State the outcome back to them either way:- Values file (
-f my-values.yaml) → note its path. It gets passed to the script via-fin the next step and tohelmitself in step 7 — the script's output only carriesbp-configurator.env, so anything else in that file (storage class, ingress, alerts flags) still needshelmto see the original file directly. Seereferences/streams.md. - Inline (
--set/--set-jsononbp-configurator.env) → the script only reads YAML files, it can't consume a--setstring. Move it into a values file first — seereferences/streams.mdfor thehelm get values -acommand (secrets included, handle with care) and why it can't be trimmed. Then treat it as the values-file case above. - No customizations → say so explicitly (e.g. "no custom
bp-configurator.envoverrides, so nothing extra is needed here") and proceed without any of the above.
- Values file (
-
Run the stream-cap script from the repo root:
bashpython3 deploy/helm/industry-profiles/warehouse-operations/scripts/compute_stream_cap.py \ --mode <mode> --num-streams <N> -o values-stream-cap.generated.yaml- If step 4 found a customizing values file, pass it here too via
-f— otherwise the generated file (built from chart defaults, layered last) silently drops those customizations. Seereferences/streams.md. - Without
--hardware-profile, it runsnvidia-smion GPU index 0 and maps the name to aHARDWARE_PROFILEusing the same table asvss-build-vision-ai's warehouse reference. If detection fails or the GPU isn't in that table, pass--hardware-profileexplicitly.IGX-THOR/DGX-SPARKedge devices aren't supported by this Helm path. - No local
nvidia-smi(runninghelm/kubectlfrom a bastion, laptop, or CI runner rather than a GPU node):kubectl execinto a GPU Operator daemonset pod (driver or device-plugin, e.g.kubectl get pods --all-namespaces -l app=nvidia-driver-daemonset) and runnvidia-smi --query-gpu=name --format=csv,noheaderthere instead, then map the name and pass--hardware-profile. - It prints the effective (possibly capped) stream count and the
syncFileCountvalue to keep in step (seereferences/streams.mdfor why). - It never lowers the request silently without saying so — a cap is always logged to stderr.
- If step 4 found a customizing values file, pass it here too via
-
Prepare the rest of the values — secrets, storage class, either ingress/
externalHostor the NodePort values file per the choice made in step 2, and — if Alerts was enabled in step 3 — the four-flag Alerts values block fromwarehouse-2d-app/README.md§Alerts (Kafka/ Elasticsearch/VST endpoints included). Onmv3dt, also add--set rtvi.vss-rtvi-cv.standaloneWarehouse.mv3dt.fusion.maxExpectedSensors=<effective-streams>(same value assyncFileCountfrom step 5). If step 4 found a customizing values file, it goes here too (-f my-values.yaml) — passing it only to the script in step 5 coversbp-configurator.envbut drops everything else in that file from the install. Seereferences/streams.mdfor the fullhelm upgrade --installcommand with the generated file layered in last via-f. -
Install/upgrade, chaining the generated file after any other
-f/--setoverrides so it wins onbp-configurator.env. The base command is the same either way; only the ingress-vs-NodePort overrides differ:bashhelm dependency update deploy/helm/industry-profiles/warehouse-operations/warehouse-<mode>-app # Ingress: helm upgrade --install wh deploy/helm/industry-profiles/warehouse-operations/warehouse-<mode>-app \ -n <namespace> --create-namespace \ --set global.vssIngress.enabled=true \ --set global.externalHost=<NODE_IP> \ --set global.storageClass=<STORAGE_CLASS> \ --set vios.vss-vios-nvstreamer.syncFileCount=<effective-streams> \ --set vios.vss-vios-nvstreamer.rtsp.instanceCount=<effective-streams> \ ... \ -f values-stream-cap.generated.yaml # last: wins on bp-configurator.env # NodePort: helm upgrade --install wh deploy/helm/industry-profiles/warehouse-operations/warehouse-<mode>-app \ -n <namespace> --create-namespace \ -f deploy/helm/industry-profiles/warehouse-operations/warehouse-<mode>-app/values-nodeport.yaml \ --set global.storageClass=<STORAGE_CLASS> \ --set vios.vss-vios-nvstreamer.syncFileCount=<effective-streams> \ --set vios.vss-vios-nvstreamer.rtsp.instanceCount=<effective-streams> \ -f values-stream-cap.generated.yaml # last: wins on bp-configurator.env...is the remaining secrets/URL overrides from step 6 — seereferences/streams.md.-f values-stream-cap.generated.yamlhas to be the last-fin the command — that's what makes it win onbp-configurator.env(multiple-ffiles merge in order given, later wins per top-level key). That includes coming aftervalues-nodeport.yamlin the NodePort case and after every other-fin both.--setdoesn't follow this rule: Helm always applies--setafter every-ffile regardless of command-line position, so a stray--setonbp-configurator.envhere would still win no matter where you put it — step 4 should already have converted any such override into a values file, not left it inline. -
Post-install validation — confirm pods actually come up before declaring success; see
warehouse-<mode>-app/README.md§Post-install validation, but don't run itskubectl get pods -w/port-forwardverbatim — those block forever. Usekubectl wait --for=condition=Ready pod --all -n <namespace> --timeout=5mand a backgroundedport-forwardinstead. -
Re-run the script whenever
NUM_STREAMSor the target GPU changes — the values-override file isn't tracked automatically; re-generate and re-helm upgradeafter a hardware change.
Prerequisites
- Kubernetes cluster reachable via
kubectl, all nodesReady. - NVIDIA GPU Operator installed, so nodes report
nvidia.com/gpuas allocatable. - StorageClass present for VST/Elasticsearch PVCs (
global.storageClass). - Helm 3.x and kubectl.
- NGC API key for the image pull secret and model/app-data download job.
- Ingress controller installed if using ingress (see the chart README's "No ingress controller: NodePort" section for the alternative).
- TURN server for WebRTC playback off-cluster (
global.turnServerUrl).
Full detail, values, and exact commands: see
deploy/helm/industry-profiles/warehouse-operations/warehouse-<mode>-app/README.md
§Prerequisites (identical across 2d/3d/mv3dt). This skill only adds the stream-cap step; it
doesn't replace chart setup — the precheck in step 1 is a fast sanity pass, not a substitute for
reading that section on first deploy.

