VSS Helm deploy
Use this workflow for the VSS sample app Helm chart at sample-applications/video-search-and-summarization/chart. The chart’s real dependencies are ovms, minioserver, audioanalyzer, postgresql, rabbitmq, videoingestion, videosearch, vdmsvectordb, multimodaldataprep, multimodalembeddingms, vectorretriever, vllm (alias of vllm-server), summaryui, and searchui (aliases of vssui).
If the user asks to map Compose or setup.sh settings to Helm values, read references/helm-values-map.md.
Answer contract when the cluster is not reachable
The user may be planning ahead, or kubectl/the cluster/the chart may be
unavailable here. In that case do not stall and do not invent output. Answer
with the exact command sequence instead: the bootstrap step, the override files
to stack in order, the values the operator must fill in, the helm install /
helm upgrade command with its namespace, and how to verify. State plainly that
the commands were not executed. Never end the answer by asking whether to run them.
Environment setup (run first)
This skill drives the Video Search & Summarization app through its real source files, so the VSS application must be present and you must run commands from its app root. Do this before anything else, and it works whether or not the VSS source is already in your workspace.
Run the bundled bootstrap. It resolves the app root in this order and prints it as the only line on stdout:
- Walk up from the current directory looking for a VSS app root - a
directory carrying all three markers
setup.sh,docker/, andpipeline-manager/. - Ask git for the enclosing repository (
git rev-parse --show-toplevel) and check whether it holdssample-applications/video-search-and-summarization, or is itself a VSS app root. This is what makes your own clone - or a fork - work unchanged. - Reuse a checkout a previous bootstrap already placed in
${XDG_CACHE_HOME:-$HOME/.cache}/vss-src/edge-ai-libraries.
If any of those hit, that checkout is reused and NO clone is performed. Only
when all three miss does it clone - and then only a shallow (--depth 1),
single-branch, sparse checkout of just
sample-applications/video-search-and-summarization from main:
# SKILL_DIR is THIS skill's own directory (shown to you when the skill loads);
# in-repo it is .github/skills/vss-deploy-helm. Works the same if the skill is installed standalone.
SKILL_DIR=".github/skills/vss-deploy-helm"
APP_ROOT="$(bash "$SKILL_DIR/scripts/vss-bootstrap.sh")"
cd "$APP_ROOT"
Every command below assumes the working directory is this APP_ROOT. To pull
from a fork/branch or reuse a specific checkout dir, override VSS_REPO_URL,
VSS_REPO_BRANCH, or VSS_CLONE_DIR before running it. The bootstrap refuses
to overwrite an existing non-VSS clone destination.
Prerequisites
- Confirm a reachable Kubernetes cluster,
kubectl, and Helm 3:kubectl cluster-info kubectl get nodes helm version - Confirm dynamic PV provisioning if using PVCs:
kubectl get storageclass - For GPU/NPU, discover resource keys before writing values:
Common Intel keys arekubectl get nodes -o json | jq -r '.items[] | "\(.metadata.name):\n" + (.status.allocatable | to_entries | map(select(.key | test("gpu|npu|vpu|accel";"i"))) | map(" \(.key): \(.value)") | join("\n"))'gpu.intel.com/i915,gpu.intel.com/xe, andnpu.intel.com/accel.
1. Start from the real chart values
Work from the chart directory:
cd sample-applications/video-search-and-summarization/chart
helm dependency update
helm dependency list
Create/edit user_values_override.yaml for user-specific values. Do not commit filled secrets.
Minimum required values for most modes:
global:
usePvc: true
keepPvc: true
huggingfaceToken: "hf_..." # needed for gated/private Hugging Face models
vlmName: "Qwen/Qwen3-VL-4B-Instruct"
llmName: "" # optional OVMS split-model summarization model
embeddingModelName: "" # set per mode below
modelDownload:
image:
repository: intel/model-download
tag: "2026.2.0-ww30"
pullPolicy: IfNotPresent
ovmsReleaseTag: "v2026.1"
proxy:
http_proxy: ""
https_proxy: ""
env:
POSTGRES_USER: "vsadmin"
POSTGRES_PASSWORD: "change-me"
MINIO_ROOT_USER: "minioadmin"
MINIO_ROOT_PASSWORD: "change-me-8chars"
RABBITMQ_DEFAULT_USER: "guest"
RABBITMQ_DEFAULT_PASS: "change-me"
# Summary/OVMS model workspace:
ovms:
claimSize: "20Gi"
# Search model caches (needed only when search is enabled):
multimodaldataprep:
modelPvc:
enabled: true
size: "10Gi"
multimodalembeddingms:
modelPvc:
enabled: true
size: "10Gi"
Why these matter:
global.usePvcenables the service-specific claims; OVMS, video-ingestion, Multimodal DataPrep, and the embedding service no longer share one PVC.global.keepPvc: trueavoids re-downloading/re-converting models after uninstall, but stale PVCs can also preserve incompatible old state. The vLLM subchart'svllm-model-cachePVC does not currently honorglobal.keepPvcand is deleted with the release.ovms.claimSizesizes the summary-mode OVMS model workspace.multimodaldataprep.modelPvcandmultimodalembeddingms.modelPvcindependently configure search model caches.global.vlmNameis required for summary/unified modes and is used by OVMS or by vLLM.global.embeddingModelNameis required when search components are enabled.global.modelDownloadcontrols the image used by the OVMS and video-ingestion init containers. Each init container starts its local REST service, submits a download job, waits for completion, and exits before the application container starts.
2. Choose the mode using the real override files
Use exactly these chart override files:
| Docker/setup concept | Helm command files | What the chart enables |
|---|---|---|
source setup.sh --summary |
-f summary_override.yaml -f user_values_override.yaml |
rabbitmq, ovms, videoingestion, audioanalyzer, summaryui; pipelinemanager.env.SUMMARY_FEATURE=FEATURE_ON |
--summary with ENABLE_VLLM=true |
-f summary_override.yaml -f xeon_vllm_values.yaml -f user_values_override.yaml |
summary mode plus vllm.enabled=true, ovms.enabled=false, pipelinemanager.env.USE_VLLM=CONFIG_ON |
source setup.sh --search |
-f search_override.yaml -f user_values_override.yaml |
multimodalembeddingms, multimodaldataprep, vdmsvectordb, vectorretriever, videosearch, searchui; global.vdmsIndexName=video_frame_embeddings |
VECTORDB_BACKEND=milvus + source setup.sh --search |
-f search_override.yaml -f search_milvus_override.yaml -f user_values_override.yaml |
switches search backend to Milvus (global.vectordbBackend=milvus), enables milvusstandalone, disables vdmsvectordb, keeps multimodaldataprep + vectorretriever + videosearch |
--summary-and-search / --all / --unified |
-f unified_summary_search.yaml -f user_values_override.yaml |
combined search+summary in one summaryui named unified-ui; global.vdmsIndexName=video_summary_embeddings |
| unified with vLLM | -f unified_summary_search.yaml -f xeon_vllm_values.yaml -f user_values_override.yaml |
unified mode plus vLLM backend |
| dual separate UIs | -f summary_override.yaml -f search_override.yaml -f user_values_override.yaml |
both summaryui and searchui; nginx routes /summary/ and /search/ |
Embedding model rule:
- Search-only and dual UI use a multimodal embedding model, for example
global.embeddingModelName: "CLIP/clip-vit-b-32". - Unified summary+search uses a text embedding model, for example
global.embeddingModelName: "QwenText/qwen3-embedding-0.6b".
3. Install
Create a namespace once:
export NAMESPACE=vss-deployment
kubectl create namespace "$NAMESPACE"
Summary with OVMS CPU:
helm install vss . \
-f summary_override.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
Summary with vLLM on Xeon CPU:
helm install vss . \
-f summary_override.yaml \
-f xeon_vllm_values.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
Search only:
helm install vss . \
-f search_override.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
Unified summary+search:
helm install vss . \
-f unified_summary_search.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
Dual separate UIs:
helm install vss . \
-f summary_override.yaml \
-f search_override.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
Before switching modes, uninstall the release first because the enabled subcharts and UI routing change:
helm uninstall vss -n "$NAMESPACE"
4. GPU/NPU and vLLM values
OVMS GPU VLM example:
global:
vlmName: "OpenVINO/Phi-3.5-vision-instruct-int8-ov"
devices:
ovms:
vlm:
device: GPU
key: "gpu.intel.com/i915"
llm:
device: CPU
key: ""
OVMS split model, e.g. GPU VLM + NPU LLM:
global:
vlmName: "OpenVINO/Phi-3.5-vision-instruct-int8-ov"
llmName: "OpenVINO/Qwen3-8B-int4-cw-ov"
devices:
ovms:
vlm:
device: GPU
key: "gpu.intel.com/i915"
llm:
device: NPU
key: "npu.intel.com/accel"
ovms:
env:
VLM_WEIGHT_FORMAT: "" # auto: CPU int8, GPU/NPU int4
LLM_WEIGHT_FORMAT: ""
Search GPU for embedding/dataprep:
global:
devices:
multimodalEmbedding:
device: GPU
key: "gpu.intel.com/i915"
multimodalDataprep:
embedding:
device: GPU
key: "gpu.intel.com/i915"
detection:
device: CPU
key: ""
Use global.devices.multimodalDataprep.embedding for in-process DataPrep
embedding, global.devices.multimodalEmbedding for the query-side embedding
service, and global.devices.multimodalDataprep.detection for DataPrep object
detection. These settings are independent; every GPU/NPU setting requires its
own resource key.
vLLM tuning keys from the actual vllm subchart:
vllm:
enabled: true
pvc:
size: 80Gi
env:
vllmCpuKvCacheSpace: "48"
vllmRpcTimeout: "100000"
vllmAllowLongMaxModelLen: "1"
vllmEngineIterationTimeoutS: "120"
vllmCpuNumReservedCpu: "0"
vllmLoggingLevel: "INFO"
model:
dtype: bfloat16
maxModelLen: 32000
maxNumBatchedTokens: 2048
maxNumSeqs: 256
tensorParallelSize: 1
resources:
requests:
cpu: "16"
memory: 128Gi
limits:
cpu: "16"
memory: 128Gi
Prefer using xeon_vllm_values.yaml rather than hand-setting all of this; it also sets pipelinemanager.env.USE_VLLM=CONFIG_ON and resource requests for dependent services.
5. Upgrade safely
After editing values, keep the same override-file stack used at install:
helm upgrade vss . \
-f summary_override.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
For vLLM summary:
helm upgrade vss . \
-f summary_override.yaml \
-f xeon_vllm_values.yaml \
-f user_values_override.yaml \
-n "$NAMESPACE"
If changing subchart code or dependencies:
helm dependency update
6. Verify
Watch pods; first startup may take 20–50 minutes because models are downloaded/converted:
kubectl get pods -n "$NAMESPACE" -w
kubectl get svc -n "$NAMESPACE"
Get the NodePort URL. The release name vss makes nginx service vss-nginx:
VSS_HOST=$(kubectl get pods -l app=vss-nginx -n "$NAMESPACE" -o jsonpath='{.items[0].status.hostIP}')
VSS_PORT=$(kubectl get service vss-nginx -n "$NAMESPACE" -o jsonpath='{.spec.ports[0].nodePort}')
echo "http://${VSS_HOST}:${VSS_PORT}"
UI paths:
- Summary/search/unified singleton modes:
/ - Dual UI mode:
/summary/and/search/; root redirects to/summary/
Check logs for slow or failed startup:
kubectl logs -n "$NAMESPACE" deploy/vss-pipelinemanager
kubectl logs -n "$NAMESPACE" deploy/vss-nginx
kubectl get events -n "$NAMESPACE" --sort-by=.lastTimestamp
When OVMS or video ingestion is stuck in Init, inspect the pod's
model-download init container:
kubectl describe pod -n "$NAMESPACE" <pod-name>
kubectl logs -n "$NAMESPACE" <ovms-pod> -c download-vlm
kubectl logs -n "$NAMESPACE" <ovms-pod> -c download-llm # split-model mode only
kubectl logs -n "$NAMESPACE" <video-ingestion-pod> -c od-model-downloader
OVMS metrics, when ovms.enabled=true:
kubectl port-forward svc/vss-nginx 8081:80 -n "$NAMESPACE"
curl http://localhost:8081/ovms/metrics
7. Common fixes
- Helm fails with missing credentials: fill
global.env.POSTGRES_USER,global.env.POSTGRES_PASSWORD,global.env.MINIO_ROOT_USER,global.env.MINIO_ROOT_PASSWORD,global.env.RABBITMQ_DEFAULT_USER,global.env.RABBITMQ_DEFAULT_PASS. - Helm fails with GPU key errors: set
global.devices.*.keyfor every non-CPU device. - Model download job/init container fails: inspect the specific model-download
log, verify
global.modelDownload.image, proxy/token values, model id, device support, and available model storage before debugging the main container. - Helm fails with a missing device key: set the matching
keyfor anyglobal.devices.*entry set toGPUorNPU. - Search returns bad/no results: confirm
global.embeddingModelNamematches the mode andglobal.vdmsIndexNamecame from the right override file. - Reinstall still broken with
global.keepPvc: true: stale PVC contents may be incompatible. Identify the affected mode and delete only its PVCs after the user accepts losing cached models/data.:# Summary with OVMS: kubectl delete pvc vss-ovms-pvc -n "$NAMESPACE" # Search model caches: kubectl delete pvc vss-multimodaldataprep-models-pvc \ vss-multimodalembeddingms-models-pvc -n "$NAMESPACE" - Need larger storage: set
ovms.claimSizefor converted VLM/LLM models,videoingestion.claimSizefor OD models,multimodaldataprep.modelPvc.size/multimodalembeddingms.modelPvc.sizefor search model caches, or the relevant data setting such asminioserver.claimSize,postgresql.claimSize,vdmsvectordb.claimSize, orvllm.pvc.size.