Skip to navigation

Helmfile Installation

View as MarkdownOpen in Claude

This section covers manual Helmfile installation of the NVCF control plane and GPU cluster components for self-hosted NVCF deployments.

For a selected release, first follow Consume an NVCF release to download its stack packages. Use this Helmfile guide for explicit release control, partial recovery and direct access to Helmfile values. Existing installations must follow the upgrade notes.

Keep the extracted deploy/stacks/self-managed, nvcf-compute-plane and observability directories together. Control-plane commands run from $BUNDLE_DIR/deploy/stacks/self-managed unless otherwise noted. The directory contains Helmfile definitions, environment templates and secret templates.

GPU-cluster commands use the sibling deploy/stacks/nvcf-compute-plane Makefile from the extracted root. It registers one GPU cluster at a time and installs the NVCA operator into that cluster. Use an installed nvcf-cli binary. Do not substitute stack directories from current main.

cd "$BUNDLE_DIR/deploy/stacks/self-managed"
ls
# Expected contents: helmfile.d/ environments/ secrets/ global.yaml.gotmpl ...

The local-development quickstart uses a source checkout and local k3d tooling; it does not select this umbrella bundle.

Namespace Requirements

Each control-plane Helm chart must be installed into a specific namespace. The control-plane namespace assignments are fixed because service-to-service DNS addressing and Vault (OpenBao) authentication claims depend on them. The observability stack uses monitoring by default, but its namespace is configurable.

NamespaceServices
nvcfapi, invocation-service, grpc-proxy, notary-service, reval, state-metrics, function-autoscaler
api-keysapi-keys, admin-issuer-proxy
essess-api
sissis
vault-systemopenbao-server
cassandra-systemcassandra
nats-systemnats
cert-managercert-manager
monitoring (default)OpenTelemetry Operator, collector, default monitors, VictoriaMetrics
envoy-gateway-systemingress (nvcf-gateway-routes)

Installing a chart into the wrong namespace will cause authentication failures such as error validating claims: claim "/kubernetes.io/namespace" does not match any associated bound claim values. If you see this error, verify that each control-plane release uses the required namespace and each observability release uses its configured namespace.

Prerequisites

Required Tools and Software

The following tools must be installed on your deployment machine:

  • kubectl
  • helm >= 3.12
  • helmfile >= 1.1.0 (recommended: 1.1.x)
  • helm-diff plugin >=3.11

Avoid Helmfile 1.2.x. Helmfile 1.2.0 removed sequential execution mode, which the NVCF stack requires for ordered deployments. Use version 1.1.x for compatibility with the commands in this guide.

Helmfile 1.3.0+ re-introduced sequential execution via the --sequential-helmfiles flag, but the command syntax differs from the 1.1.x examples shown here. If you choose to use 1.3.0+, add --sequential-helmfiles to every helmfile apply and helmfile sync command.

  • A kubernetes cluster (CSP agnostic or on-prem).
  • Gateway API ingress prepared as described in Gateway quickstart if you are exposing NVCF through Gateway API
  • Configure authenticated customer chart and image access for the selected artifacts, or use an accessible mirror. Workload artifacts use their own registry credentials.
  • Download the selected stack packages, and keep the stack directories together.
# Install helm-diff plugin (required for helmfile)
helm plugin install https://github.com/databus23/helm-diff

Use a kubectl client within one minor version of the Kubernetes API server, as required by the Kubernetes version-skew policy. A client outside that range is unsupported, even if individual commands work. Check both versions before installing, especially after a package-manager update:

kubectl version

For example, kubectl 1.36 supports Kubernetes API servers 1.35 through 1.37. If your client is outside the supported range, install a compatible version from the Kubernetes tools page.

Access Requirements

  • kubectl configured to the kubernetes cluster you are deploying to

  • Personal NGC API Key from ngc.nvidia.com authenticated with nvcf-onprem organization only if you pull artifacts directly from NGC or use NGC as your registry

  • Registry credentials for your container registry (ECR, NGC, etc.). See third-party-registries-self-hosted for setup instructions

  • Local Helm/Docker authentication to your container registry where NVCF charts are stored. Helmfile pulls OCI charts during deployment, so your local environment must be authenticated. Examples:

    • AWS ECR: aws ecr get-login-password --region <region> | helm registry login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
    • NGC: docker login nvcr.io -u '$oauthtoken' -p <NGC_API_KEY>
    • Other registries: Use docker login or helm registry login as appropriate for your registry

If you are using NGC as your registry, you will use your NGC API key when generating the base64 registry credential in Step 3. Exporting NGC_API_KEY is optional and only needed if you prefer to reuse it in commands.

Installation Steps

The installation flow is as follows.

  1. Prepare Gateway API ingress
  2. Configure your environment file (environments/<environment-name>.yaml)
  3. Configure your secrets file (secrets/<environment-name>-secrets.yaml)
  4. Configure image pull secrets (skip if using a CSP registry with built-in credential helpers)
  5. Deploy the NVCF control plane components
  6. Verify the control plane
  7. Register each GPU cluster with the control plane
  8. Install the NVCA operator on each GPU cluster

Step 1. Prepare Gateway API ingress

Complete Gateway quickstart before you configure and apply the Helmfile stack.

Keep these values from the Gateway quickstart:

echo "$GATEWAY_ADDR"
echo "$HTTP_GATEWAY_NAMESPACE/$HTTP_GATEWAY_NAME"
echo "$GRPC_GATEWAY_NAMESPACE/$GRPC_GATEWAY_NAME"

Use GATEWAY_ADDR as global.domain in your environment file. Use the Gateway names, namespaces, and listener names from Gateway quickstart in ingress.gatewayApi.gateways.

Split or multi-cluster gRPC invocation is not enabled by default. If you need workers in a compute cluster to reach grpc-proxy in the control-plane cluster, complete gRPC Invocation Enablement before you deploy or sync the control plane.

Remote LLM workers use separate gRPC and reverse QUIC paths. Complete LLM worker listeners and Remote compute clusters and regions before applying the control plane.

The Gateway address is embedded throughout your deployment. The domain value in your environment file, the Gateway API HTTPRoutes/TCPRoutes, and service discovery all depend on this address. If the Gateway or its underlying load balancer is deleted and recreated (e.g., due to a TCPRoute misconfiguration), a new address will be assigned.

If the address changes after deployment, you must update the domain in your environment file and re-sync the affected releases. See Recovering from Gateway Address Changes for the procedure.

Step 2. Configure your environment file (environments/<environment-name>.yaml)

Environment configuration files define how NVCF is deployed in your specific environment. They are YAML files that provide values to the Helm charts.

Set HELMFILE_ENV to your environment name and copy the base configuration. The filename must match HELMFILE_ENV because Helmfile uses it to select the environment file. The template below shows the values to configure for Amazon EKS (cp-env-eks-example.yaml).

cd "$BUNDLE_DIR/deploy/stacks/self-managed"
export HELMFILE_ENV="<environment-name>"
cp environments/base.yaml "environments/${HELMFILE_ENV}.yaml"

The following example shows a typical configuration for Amazon EKS:

environments/eks-example.yaml
global:
# Domain for external access (used by Gateway API HTTPRoutes)
domain: "GATEWAY_ADDR" # Replace with ELB domain
# =============================================================================
# Helm Chart Sources Configuration
# =============================================================================
# Configure the OCI registry where NVCF Helm charts are stored.
# This must point to a registry containing the NVCF chart packages.
# =============================================================================
helm:
sources:
registry: <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
repository: <your-ecr-repository-name>
# NGC Example:
# registry: nvcr.io
# repository: YOUR_ORG/YOUR_TEAM # e.g. 123456789102/YOUR_TEAM
# ECR Example:
# registry: <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
# repository: <your-ecr-repository-name>
# =============================================================================
# Container Image Registry Configuration
# =============================================================================
# Configure the container registry where NVCF service images are stored.
# These images are pulled by Kubernetes when deploying the NVCF stack.
# =============================================================================
image:
registry: <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
repository: <your-ecr-repository-name>
# NGC Example:
# registry: nvcr.io
# repository: YOUR_ORG/YOUR_TEAM # e.g. 123456789102/YOUR_TEAM
# ECR Example:
# registry: <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
# repository: <your-ecr-repository-name>
workerEndpoints:
# Optional. Empty uses the cluster-local request-router service. Set a
# worker-reachable host and port for a split-cluster deployment.
llmRequestRouterAddress: ""
nodeSelectors:
enabled: true # Set true when using dedicated node labels for NVCF workloads
vault:
key: nvcf.nvidia.com/workload
value: vault
cassandra:
key: nvcf.nvidia.com/workload
value: cassandra
controlplane:
key: nvcf.nvidia.com/workload
value: control-plane
storageClass: "gp3" # Customize to your storage class
storageSize: "10Gi" # Customize to your storage size
# =============================================================================
# Observability Configuration
# =============================================================================
# Enable distributed tracing via OTLP (disabled by default).
# This must point to an OTLP-compatible collector.
# =============================================================================
observability:
tracing:
enabled: false
collectorEndpoint: ""
collectorPort: 4317
collectorProtocol: http
# Example:
# enabled: true
# collectorEndpoint: <your-collector-endpoint>
# collectorPort: <your-collector-port>
# collectorProtocol: <your-collector-protocol>
# Install control-plane monitors, the bundled metrics backend, and the
# Function Autoscaler.
observability:
profile: control
victoriaMetrics:
server:
persistentVolume:
enabled: true
size: 16Gi
storageClass: "gp3" # Customize to your storage class.
fakeGpuOperator:
enabled: false # If deploying locally with no GPUs, true
ubuntu:
imageName: alpine-k8s
tag: 1.30.12
accounts: # Default NVCF account configuration
limits:
maxFunctions: 10
maxTasks: 10 # Note: Tasks (NVCT) are not currently supported for EA
maxTelemetries: 10
maxRegistryCreds: 10
# These static global values are processed in the values template
nats:
enabled: true
cassandra:
enabled: true
openbao:
enabled: true
migrations:
issuerDiscovery:
enabled: true # Recommended true for EKS - discovers OIDC issuer automatically
# Ingress Gateway Configuration
ingress:
gatewayApi:
enabled: true
controllerNamespace: "envoy-gateway-system" # must be set by the environment
routes:
nvcfApi:
routeAnnotations: {}
apiKeys:
routeAnnotations: {}
invocation:
routeAnnotations: {}
grpc:
routeAnnotations: {}
gateways:
shared:
name: "nvcf-gateway" # must be set by the environment
namespace: "envoy-gateway" # must be set by the environment
listenerName: http
grpc:
name: "nvcf-gateway" # must be set by the environment
namespace: "envoy-gateway" # must be set by the environment
listenerName: tcp

When addons.llm is enabled, the stack defaults global.workerEndpoints.llmRequestRouterAddress to llm-request-router-backend-router.nvcf.svc.cluster.local:50071 when backend routing is enabled, otherwise llm-request-router.nvcf.svc.cluster.local:50071. For a split deployment, this address alone is not enough. Configure the paired backend-router gRPC and reverse QUIC dial addresses, Gateway routes, DNS, and trust described in Remote compute clusters and regions.

observability Configuration

The self-managed control-plane stack defaults to observability.profile: control. This installs the shared metrics components, VictoriaMetrics, State Metrics, and the Function Autoscaler. Set the VictoriaMetrics storage class for the target cluster.

To use a customer-managed backend or change component ownership, see Observability Configuration. For autoscaler health and backend checks, see Function Autoscaler Operations.

domain and ingress Configuration

The domain and ingress sections of the environment file are used to configure the external access to the NVCF control plane.

If using the above example directly for EKS, replace GATEWAY_ADDR with the Gateway load balancer address from Gateway quickstart.

domain: "GATEWAY_ADDR" # Replace with ELB domain

If using the above example directly for EKS, your ingress configuration would look like this:

ingress:
gatewayApi:
enabled: true
controllerNamespace: "envoy-gateway-system"
routes:
nvcfApi:
routeAnnotations: {}
apiKeys:
routeAnnotations: {}
invocation:
routeAnnotations: {}
grpc:
routeAnnotations: {}
gateways:
shared:
name: "nvcf-gateway"
namespace: "envoy-gateway"
listenerName: http
grpc:
name: "nvcf-gateway"
namespace: "envoy-gateway"
listenerName: tcp

nodeSelectors Configuration

The nodeSelectors section of the environment file is used to configure the nodes on which the NVCF control plane components are deployed. Disable this unless you have a cluster with node selectors pre-configured on node pools within your cluster.

If your cluster uses dedicated node labels for NVCF workloads, enable this section with the following configuration:

nodeSelectors:
enabled: true
vault:
key: nvcf.nvidia.com/workload
value: vault
cassandra:
key: nvcf.nvidia.com/workload
value: cassandra
controlplane:
key: nvcf.nvidia.com/workload
value: control-plane

cassandra Resource Tuning

Cassandra needs enough memory to complete first boot, commit-log replay, and the schema migration hooks. The default self-managed stack uses cassandra.resourcesPreset: xlarge, which maps to a Bitnami Cassandra preset with a 3 GiB memory request and a 6 GiB memory limit. Do not use the small preset for cloud installs. It can OOM-kill Cassandra during initialization and cause migration failures.

Common preset values:

PresetRequestsLimits
small500m CPU, 512Mi memory750m CPU, 768Mi memory
large1 CPU, 2048Mi memory1.5 CPU, 3072Mi memory
xlarge1 CPU, 3072Mi memory3 CPU, 6144Mi memory
2xlarge1 CPU, 3072Mi memory6 CPU, 12288Mi memory

All listed presets include a 50Mi ephemeral-storage request and 2Gi ephemeral-storage limit.

If Cassandra pods restart with OOMKilled, or the cassandra-migrations job fails with a consistency-level error while Cassandra pods are restarting, increase the preset in your environment file:

cassandra:
resourcesPreset: "2xlarge"

Then apply the change to just Cassandra:

HELMFILE_ENV=<environment-name> helmfile --selector name=cassandra sync

For local development, a lower preset may be acceptable when the environment also reduces Cassandra to one replica. For cloud installs, start with xlarge or higher and tune from there.

OpenBao auto-unseal

By default OpenBao uses a Shamir seal: the unseal key sits in the Secret openbao-server-unseal, and a sidecar unseals each pod with it. An auto-unseal seal, such as a cloud KMS, keeps the unseal key off the cluster. Set it before the first install, so the init hook initializes OpenBao with recovery keys instead of an unseal key:

openbao:
server:
autoUnseal:
enabled: true
ha:
raft:
config: |
<the whole config from the chart's auto-unseal example, with your seal stanza>
  • With autoUnseal.enabled, the stack also drops the Shamir unseal sidecar and its Secret volume.
  • ha.raft.config replaces the chart’s whole Raft config, so it must also carry the storage and listener blocks. Copy it from the chart’s auto-unseal example, which has the default config with an AWS KMS seal, and change only the seal stanza.
  • Before the first install, give the openbao-server service account in vault-system the provider’s rights on the key; the init hook needs them and runs only once. For AWS KMS that is kms:Encrypt, kms:Decrypt, and kms:DescribeKey, for example through an EKS Pod Identity association. For IRSA or GKE Workload Identity, annotate the account through openbao.server.serviceAccount.annotations, such as eks.amazonaws.com/role-arn.
  • After the first install, the init hook leaves every recovery share in the Secret openbao-server-recovery-keys. Export the shares, give them to separate custodians, and delete the Secret.

Set auto-unseal only on a new install. On an installed cluster, helmfile apply with these values succeeds and changes nothing yet, because the OpenBao StatefulSet uses OnDelete. Each OpenBao pod restarted later, for example by a node drain, comes up sealed and waits for a seal migration. A sealed pod still reports Ready, so further drains can take OpenBao below Raft quorum. On a cluster first installed with auto-unseal, removing these values, or rolling the stack back to a version that does not forward them, brings back a volume for an unseal Secret that was never created, so restarted pods cannot start. Change the seal of an installed cluster only through OpenBao seal migration, one pod at a time.

helm and image Configuration

Set the chart and image sources in the environment’s global section. For the public NVCF catalog:

global:
helm:
sources:
url: https://helm.ngc.nvidia.com/nvidia/nvcf
username: "$oauthtoken"
password: "<customer NGC API key>"
image:
registry: nvcr.io
repository: nvidia/nvcf
imagePullSecrets:
- name: nvcr-pull-secret

Keep credential-bearing files out of source control. For a private mirror, omit url and set the mirror’s registry and repository instead, as in the EKS example above. Keep one global mapping.

Before upgrading a mirrored deployment, copy every selected chart and image that changed. Use the artifact manifest as the version list and Image Mirroring for copy commands. Registry locations do not supply Kubernetes pull credentials; follow Configure image pull secrets for each namespace that pulls from a private registry.

Migration clients on Kubernetes 1.35

The selected OpenBao migration image includes kubectl 1.37.2 by default and 1.36.6 for Kubernetes 1.35 clusters. In the control-plane environment, select the compatible client before rendering or applying:

openbao:
migrations:
kubectlMinor: "1.36"

This setting is inherited by OpenBao migrations and enabled SIS HMAC rotation, LLM PKI and UI migration hooks. Explicit per-hook kubectlMinor settings take precedence; remove stale overrides when all hooks should use the shared choice. Review rendered Jobs for NVCF_KUBECTL_MINOR: "1.36" in their environment.

The selector does not detect the server version automatically. kubectl must stay within one minor of the API server; follow the Kubernetes version-skew policy. The independent upstream account-bootstrap and upgrade-receipt helpers do not use this selector. Configure their image versions separately as described in upstream helper images.

Overriding Helm Chart Values

The environment file (environments/<environment-name>.yaml) controls global settings like domain, image, and nodeSelectors. However, you may need to override values for a specific Helm chart, for example to increase Cassandra memory limits or change an image tag for one service.

Helmfile releases support a values property that passes values through to the underlying helm install/helm upgrade command. To add chart-specific overrides, edit the release definition in the appropriate file under helmfile.d/ and add a values block:

# Example: helmfile.d/01-dependencies.yaml.gotmpl
- name: cassandra
version: 0.9.0
condition: cassandra.enabled
namespace: cassandra-system
<<: *dependency
values:
- ../global.yaml.gotmpl
- ../secrets/{{ requiredEnv "HELMFILE_ENV" }}-secrets.yaml
- cassandra:
resources:
requests:
cpu: "2"
memory: 4096Mi
limits:
cpu: "8"
memory: 8192Mi

When a release inherits from a template (<<: *dependency), specifying values on the release replaces the template’s values list. YAML merge does not append lists. You must re-include global.yaml.gotmpl and the secrets file.

The values block is a list of YAML mappings. Keys correspond to the chart’s values.yaml structure. For example, to override a deeply nested value:

values:
- api:
image:
tag: 2.223.9
env:
NVCF_REGISTRIES_ACCOUNT_PROVISIONING_ARTIFACT_TYPES: "CONTAINER,HELM"

Values defined here take the highest precedence, overriding both the environment file and global.yaml.gotmpl. Use helmfile template to preview the rendered manifests after adding overrides, then apply to a single release:

# Preview changes
HELMFILE_ENV=<environment-name> helmfile --selector name=cassandra template
# Apply changes to just that release
HELMFILE_ENV=<environment-name> helmfile --selector name=cassandra sync

Upgrade cert-manager image pins

When upgrading to helm-nvcf-cert-manager 0.1.1, check the Helmfile environment for explicit cert-manager image overrides. The chart defaults to v1.20.5, but an explicit tag or digest takes precedence. A retained v1.20.2 digest can keep the old image even when its tag says v1.20.5.

Set both tag: "v1.20.5" and the matching digest for each image under certManager.image, certManager.webhook.image, certManager.cainjector.image, certManager.startupapicheck.image, and certManager.acmesolver.image. Use these multi-platform digests when the repositories point to the upstream Quay images:

ImageDigest
cert-manager-controllersha256:62df7490e10bc668edc34d4083a5439b4b684073eb78ddadf58cf35e655d8298
cert-manager-webhooksha256:85367b4b5a6662e5a05c0f2465815e2dd9e4eb1957a2123be2b20f9de5135252
cert-manager-cainjectorsha256:d08d2695ad3714ba42a04c07f3a15fef6e1accd02716a5ac66ba1fa8f99afca6
cert-manager-startupapichecksha256:edbbd82da8e9110bc2f2ee8b4a6ef0155cbc097c0dbc0b85f8b6cd2f8c96edb3
cert-manager-acmesolversha256:ad8df9de893f0200eec00f288550ddd82b99f84aa4085b8dc0339758bb119fac

For private mirrors, use the digest of each image in the destination registry. Upgrade only after the 0.1.1 chart and all mirrored v1.20.5 images are published. From the extracted stack bundle root, render the cert-manager dependency stage. Confirm the four pod images and the controller’s --acme-http01-solver-image argument use v1.20.5:

HELMFILE_ENV=<environment-name> helmfile \
-f helmfile.d/01-dependencies.yaml.gotmpl \
--selector name=cert-manager template

Worker Image Version Overrides

The NVCF API uses the worker image versions in its nvcf.sidecars.* configuration. To pin worker sidecars for one Helmfile deployment, add an inline remote-config override to the api release in helmfile.d/02-core.yaml.gotmpl:

- name: api
version: 1.19.3
namespace: nvcf
inherit:
- template: service
values:
- ../global.yaml.gotmpl
- ../secrets/{{ requiredEnv "HELMFILE_ENV" }}-secrets.yaml
- api:
remoteConfig:
enabled: true
configData:
nvcf:
sidecars:
init-container: "${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/nvcf_worker_init:<tag>"
utils-container-image:
go: "${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/nvcf_worker_utils:<tag>"
niclls-container: "${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/nvcf_worker_niclls:<tag>"
needs:
- ess/ess-api

The ${nvcf.sidecars.hostname} and ${nvcf.sidecars.repository} placeholders resolve from the stack image registry and repository settings. Re-include global.yaml.gotmpl and the secrets file because defining values on the release replaces the inherited values list. Keep the existing needs entry on the release.

Step 3. Configure your secrets file (secrets/<environment-name>-secrets.yaml)

Secrets configuration contains any sensitive data required for NVCF operation. The image pull secret credentials you insert here will be used to bootstrap the NVCF API with registry credentials for all worker components (function sidecars), function containers and helm charts.

These credentials will then be used for function deployments. Note that if the registry credentials are not correct you can always update them using the steps in third-party-registries-self-hosted.

Copy the secrets template using the same HELMFILE_ENV value from Step 2. The filename must match HELMFILE_ENV because Helmfile loads the corresponding secrets file. The example below shows the required structure (example-secrets.yaml). You must replace all instances of REPLACE_WITH_BASE64_DOCKER_CREDENTIAL with your actual base64-encoded registry credentials.

cd "$BUNDLE_DIR/deploy/stacks/self-managed"
cp secrets/secrets.yaml.template "secrets/${HELMFILE_ENV}-secrets.yaml"
secrets/example-secrets.yaml
# Required structure for any environment secrets.
# This is the minimal set of values to provide.
# Notes:
# Cassandra:
# The password should match the value set in the cassandra keyspace migrations
#
# API:
# The value for the registry will be used in three places, as it is
# expected the same registry is used as a single source for all images.
# openbao.migrations.env[1].value
# api.accountBootstrap.registryCredentials[0].secret.value
# api.accountBootstrap.registryCredentials[1].secret.value
openbao:
migrations:
env:
# Stored in OpenBao shared secrets (written by migration job)
- name: DEFAULT_CASSANDRA_PASSWORD
value: "ch@ng3m3"
# Stored in OpenBao KV for nvcf-api (written by migration job)
- name: NVCF_API_SIDECARS_IMAGE_PULL_SECRET
value: REPLACE_WITH_BASE64_DOCKER_CREDENTIAL # Replace with base64 credentials (ex. NGC / ECR / etc.) for your registry, refer to Working with Third-Party Registries.
- name: ADMIN_CLIENT_ID
value: ncp # <- keep this value
api:
accountBootstrap:
registryCredentials:
- registryHostname: nvcr.io # ECR: <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
secret:
name: nvcr-containers # ECR: ecr-containers
value: REPLACE_WITH_BASE64_DOCKER_CREDENTIAL # Replace with base64 credentials (ex. NGC / ECR / etc.) for your registry, refer to Working with Third-Party Registries.
artifactTypes: ["CONTAINER"]
tags: []
description: "NGC Container registry"
- registryHostname: helm.ngc.nvidia.com # ECR: <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
secret:
name: nvcr-helmcharts # ECR: ecr-helmcharts
value: REPLACE_WITH_BASE64_DOCKER_CREDENTIAL # Replace with base64 credentials (ex. NGC / ECR / etc.) for your registry, refer to Working with Third-Party Registries.
artifactTypes: ["HELM"]
tags: []
description: "NGC Helm registry"

NVCF supports these registries for function containers (set in api.accountBootstrap.registryCredentials): ACR (Azure), ECR (AWS), NVCR (NVIDIA), VolcEngine CR, JFrog/Artifactory, and Harbor.

Generating Base64-encoded Registry Credentials

Registry credentials must be base64-encoded in the format username:password. For detailed instructions on setting up credentials for specific registries (including IAM user creation for ECR), see third-party-registries-self-hosted.

# Replace YOUR_NGC_API_KEY with your actual personal NGC API key from ngc.nvidia.com
printf '%s' '$oauthtoken:YOUR_NGC_API_KEY' | base64 | tr -d '\n'

Set kubectl to the control-plane cluster context before proceeding. Steps 4 and 5 run kubectl and helmfile commands that target the current context. In a multi-cluster setup, verify the context is the control-plane cluster to avoid installing to the wrong cluster.

kubectl config use-context <control-plane-context>
kubectl config current-context

Step 4. Configure image pull secrets (conditional)

Skip this step if you have mirrored NVCF artifacts to a CSP-managed registry (e.g., ECR) and are using a CSP-managed registry with built-in credential helpers (e.g., AWS ECR with IAM node roles, GKE Artifact Registry with Workload Identity, Azure ACR with managed identity). Kubernetes can pull images automatically in those environments.

The secrets file you configured in Step 3 handles API bootstrap registry credentials. These allow the NVCF API service to pull user function containers at runtime. Separately, Kubernetes itself needs image pull secrets to pull the NVCF control plane service images (API, SIS, Cassandra, etc.) from your registry.

If your image registry is private and your cluster nodes do not have built-in credential helpers, you must create Kubernetes docker-registry secrets in each NVCF namespace and configure the helmfile to reference them.

  1. Create the pull secret in each NVCF namespace (create-nvcr-pull-secrets.sh):
export NGC_API_KEY="<your-ngc-api-key>"
for ns in cassandra-system nats-system nvcf api-keys ess sis \
vault-system cert-manager; do
kubectl create namespace "$ns" --dry-run=client -o yaml | kubectl apply -f -
done
for ns in cassandra-system nats-system nvcf api-keys ess sis \
vault-system cert-manager; do
kubectl create secret docker-registry nvcr-pull-secret \
--docker-server=nvcr.io \
--docker-username='$oauthtoken' \
--docker-password="$NGC_API_KEY" \
--namespace="$ns" \
--dry-run=client -o yaml | kubectl apply -f -
done

For registries other than NGC, replace --docker-server, --docker-username, and --docker-password with your registry credentials.

  1. Reference the secret in your Helmfile environment. The Helmfile propagates imagePullSecrets to all NVCF charts automatically. Add the secret name to your environment YAML (e.g. environments/<your-env>.yaml):
global:
imagePullSecrets:
- name: nvcr-pull-secret

This replaces any need for a separate admission controller or policy engine to inject pull secrets.

Step 5. Deploy the NVCF control plane components

Confirm your kubectl context is still set to the control-plane cluster (see above).

Ensure your local environment is authenticated to the container registry where your NVCF Helm charts are stored (see Access Requirements). Helmfile pulls OCI charts during deployment and will fail if not authenticated.

Before deploying, preview the rendered Kubernetes manifests:

cd "$BUNDLE_DIR/deploy/stacks/self-managed"
HELMFILE_ENV=<environment-name> helmfile template

This command will:

  1. Render all Helm charts with your environment and secrets
  2. Run validation hooks
  3. Display the resulting Kubernetes manifests

Review the output carefully to ensure:

  • Container image references are correct
  • Storage classes match your clusters

Deploy the self-managed stack:

HELMFILE_ENV=<environment-name> helmfile sync

The initial deployment takes approximately 5-10 minutes for local development and 10-20 minutes for cloud deployments.

Deployment Progression and Monitoring

Helmfile will deploy services in the correct order with dependencies:

Phase 1: dependency layer (5-10 minutes)

  • NATS messaging service
  • OpenBao (secrets management)
  • Cassandra (database)
  • Helmfile selector: release-group=dependencies

Phase 2: control-plane services (5-10 minutes)

  • NVCF API Service
  • SIS (Spot Instance Service)
  • gRPC Proxy
  • Invocation Service
  • API Keys Service
  • ESS API
  • Notary Service
  • Admin Issuer Proxy
  • Helmfile selector: release-group=services

Monitor for account bootstrap failures. Once Helmfile reaches Phase 3, open a separate terminal and watch events in the nvcf namespace:

kubectl get events -n nvcf -w

The account bootstrap job runs as a post-install hook and is the most common failure point, usually due to environment or secrets misconfiguration. If it fails, see Recovering from Partial Deployments for recovery steps.

Phase 3: ingress configuration (1-2 minutes)

  • Gateway API Routes (if enabled)
  • Helmfile selector: release-group=ingress

GPU clusters are installed after the control plane succeeds. Use Step 7. Configure the compute-plane Helmfile environment and Step 8. Register and install each GPU cluster for the split compute-plane bundle.

Open a separate terminal to monitor the deployment progress:

Monitor each deployment phase:

# Check namespace creation and preparation
kubectl get ns
# Phase 1: Check dependency services (release-group=dependencies)
kubectl get pods -n nats-system # Should see nats-0, nats-1, nats-2
kubectl get pods -n vault-system # Should see openbao-server-0, openbao-server-1, openbao-server-2
kubectl get pods -n cassandra-system # Should see cassandra-0, cassandra-1, cassandra-2
# Note: It's normal to see cassandra-initialize-cluster pods with "Error" status.
# The initialization job retries on failure - as long as one pod shows "Completed"
# and cassandra-migrations is Running/Completed, the deployment is progressing normally.
# Phase 2: Check control plane services (release-group=services)
kubectl get events -n nvcf -w # Watch for account bootstrap failures
kubectl get pods -n nvcf # API, invocation-service, grpc-proxy, notary-service
kubectl get pods -n sis # Spot Instance Service
kubectl get pods -n api-keys # API Keys service, admin-issuer-proxy
...
# Phase 3: Check ingress (release-group=ingress)
kubectl get httproutes -A # Gateway API routes (if enabled)

Cassandra initialization pods showing Error is expected. The cassandra-initialize-cluster job runs multiple pods in parallel and retries on failure. It is normal to see one or more pods with Error status. The deployment is healthy as long as at least one initialization pod reaches Completed and the cassandra-migrations job completes successfully.

If any pod remains in Pending, ContainerCreating, or ImagePullBackOff state for more than 5 minutes, see self-hosted-troubleshooting for issue identification commands and solutions.

Recovering from Partial Deployments

Do not attempt to fix a partially failed deployment by re-running helmfile sync or helmfile apply. Helm releases in a failed state will skip initialization hooks on subsequent runs, leading to incomplete deployments that appear successful but don’t function correctly.

Redeploying dependencies if needed:

If a dependency service (Cassandra, NATS, OpenBao) fails or gets stuck, you can safely redeploy it individually:

# Redeploy only Cassandra
HELMFILE_ENV=<environment-name> helmfile --selector name=cassandra apply
# Redeploy all dependencies (NATS, Cassandra, OpenBao)
HELMFILE_ENV=<environment-name> helmfile --selector release-group=dependencies apply

Recovering from services failures without destroying dependencies:

If the release-group=services deployment hangs or fails (for example, account bootstrap failure due to secrets misconfiguration), you can recover without destroying your dependencies.

  1. Monitor for failures:

In a separate terminal, watch events in the nvcf namespace:

kubectl get events -n nvcf -w
  1. Check the account bootstrap logs if it failed:
kubectl logs job/nvcf-api-account-bootstrap -n nvcf

The bootstrap job auto-deletes after ~5 minutes. Monitor events to catch failures in real-time.

  1. Check the NVCF API logs for detailed error messages:
kubectl logs -n nvcf -l app.kubernetes.io/name=nvcf-api --tail=100
  1. Fix the root cause, for example correct your secrets/<environment-name>-secrets.yaml file.

  2. Destroy the services and downstream releases:

# Destroy services release group
HELMFILE_ENV=<environment-name> helmfile --selector release-group=services destroy
# Destroy downstream releases (ingress, admin-issuer-proxy)
HELMFILE_ENV=<environment-name> helmfile --selector release-group=ingress destroy
HELMFILE_ENV=<environment-name> helmfile --selector name=admin-issuer-proxy destroy
  1. Clean up the service namespaces:
kubectl delete namespace nvcf api-keys ess sis --ignore-not-found
  1. Recreate namespaces and labels. Gateway API routing requires these labels:
kubectl create namespace api-keys && \
kubectl create namespace ess && \
kubectl create namespace sis && \
kubectl create namespace nvcf
kubectl label namespace api-keys nvcf/platform=true && \
kubectl label namespace sis nvcf/platform=true && \
kubectl label namespace ess nvcf/platform=true && \
kubectl label namespace nvcf nvcf/platform=true
  1. Re-sync services. This triggers fresh post-install hooks:
HELMFILE_ENV=<environment-name> helmfile --selector release-group=services sync
  1. Sync remaining releases after services succeed:
HELMFILE_ENV=<environment-name> helmfile --selector name=admin-issuer-proxy sync
HELMFILE_ENV=<environment-name> helmfile --selector release-group=ingress sync

Full restart if dependencies are also broken:

If dependencies are corrupted or you prefer a clean slate, follow the complete Uninstalling steps, fix your configuration, then redeploy from Step 1.

Enabling Vanity Gateway

Vanity Gateway is optional and disabled by default. It is available only in stack packages that include the Vanity Gateway addon. If your extracted stack package does not contain a vanity-gateway release and vanityGateway route values, skip this section until you use a stack package that includes them.

Enable it only when you need a customer-facing hostname or path mapping layer in front of the standard NVCF service routes.

In stack packages that include the addon, set the value shape in your environment file:

addons:
vanityGateway:
enabled: true
mappingConfig: {}

By default, the route host is vanity.<domain> and the backend is vanity-gateway.nvcf:8080. Put deployment-specific host and path mappings under addons.vanityGateway.mappingConfig. If your deployment needs custom vanity hosts, use the route hostname overrides supported by your stack package and create matching DNS records.

Model API request bodies default to 25 MiB (26214400 bytes). Set addons.vanityGateway.config.maxRequestSizeBytes to a positive integer in bytes to change the limit. For example, allow 64 MiB:

addons:
vanityGateway:
enabled: true
config:
maxRequestSizeBytes: 67108864

The gateway holds each model API request body in memory while it routes the request. LLM Gateway routes and shadow models add more copies. In one in-process test, a single 64 MiB request raised gateway heap use by about 224 MiB on a direct route, 600 to 700 MiB on an LLM Gateway route, and about 900 MiB with one shadow model. Treat these numbers as examples, not limits. When raising this limit, also raise addons.vanityGateway.resources.limits.memory. Size it from a load test at your expected concurrency, with headroom. The chart’s default memory limit is 512Mi.

Models with functionType: LLM are served through the LLM Gateway, which rejects request bodies over 50 MiB (52428800 bytes) by default. The stack does not change that limit, so a Vanity Gateway limit above 50 MiB does not help these models.

The stack passes this value to the chart’s vanityGateway.config.maxRequestSizeBytes, which sets the service environment variable MAX_REQUEST_SIZE_BYTES. Invalid or non-positive values are rejected. The limit covers model API POST routes, including image endpoints. Generic vanity pass-through routes and private-model lookup response limits are unchanged.

helmfile apply updates the gateway ConfigMap, but running pods keep the old environment until they restart. A change to pod resources, such as the memory limit, restarts them. Otherwise, restart them yourself:

kubectl rollout restart deployment/vanity-gateway -n nvcf

After confirming your stack package includes the vanity-gateway release, preview and apply the service plus gateway routes:

HELMFILE_ENV=<environment-name> helmfile --selector name=vanity-gateway template
HELMFILE_ENV=<environment-name> helmfile --selector name=vanity-gateway sync
HELMFILE_ENV=<environment-name> helmfile --selector release-group=ingress sync

Verify only when the addon is present and enabled:

kubectl get deploy,svc -n nvcf -l app.kubernetes.io/name=vanity-gateway
kubectl get httproute -A | grep -i vanity
curl -H "Host: vanity.<domain>" "http://<gateway-address>/health"

Enabling NVCF UI

NVCF UI is optional and disabled by default. It is available only in stack packages that include the NVCF UI addon. If your extracted stack package does not contain a nvcf-ui release and nvcfUi route values, skip this section until you use a stack package that includes them.

Enable it only when you need a customer-facing NVCF admin-panel UI. For a standalone walkthrough, see Enabling NVCF UI.

The NVCF UI admin panel is currently unauthenticated. Do not expose it to the public internet. Restrict access to a trusted network, VPN, or an authenticating proxy in front of the nvcf-ui route.

In stack packages that include the addon, set the value shape in your environment file:

addons:
nvcfUi:
enabled: true

Configure the conditional image pull secret. nvcf-ui runs in its own nvcf-ui namespace, which is separate from the other namespaces covered in Step 4. Configure image pull secrets (conditional). If your image registry is private and your cluster nodes do not have built-in credential helpers, Kubernetes needs a docker-registry type secret in the nvcf-ui namespace to pull the nvcf-ui image. Skip this step if you mirrored NVCF artifacts to a CSP-managed registry with built-in credential helpers, as described in Step 4.

export NGC_API_KEY="<your-ngc-api-key>"
kubectl create namespace nvcf-ui --dry-run=client -o yaml | kubectl apply -f -
kubectl create secret docker-registry nvcr-pull-secret \
--docker-server=nvcr.io \
--docker-username='$oauthtoken' \
--docker-password="$NGC_API_KEY" \
--namespace=nvcf-ui \
--dry-run=client -o yaml | kubectl apply -f -

By default, the route host is nvcf-ui.<domain> and the backend is nvcf-ui.nvcf-ui:8300.

After confirming your stack package includes the nvcf-ui release, preview and apply the service plus gateway routes:

HELMFILE_ENV=<environment-name> helmfile --selector name=nvcf-ui template
HELMFILE_ENV=<environment-name> helmfile --selector name=nvcf-ui sync
HELMFILE_ENV=<environment-name> helmfile --selector release-group=ingress sync

Verify only when the addon is present and enabled:

kubectl get deploy,svc -n nvcf-ui
kubectl get httproute -A | grep -i nvcf-ui
curl -i -H "Host: nvcf-ui.<domain>" "http://<gateway-address>/status"

Recovering from Gateway Address Changes

If your Gateway or its underlying load balancer was deleted and recreated (e.g., due to a TCPRoute misconfiguration or infrastructure change), the external address will change. Services that depend on the domain value — including Gateway API routes, SIS cluster registration, API hostname resolution, and the optional Vanity Gateway route — will break until the new address is propagated.

  1. Get the new Gateway address:
GATEWAY_ADDR=$(kubectl get gateway nvcf-gateway -n envoy-gateway -o jsonpath='{.status.addresses[0].value}')
echo "$GATEWAY_ADDR"
  1. Update your environment file with the new address:
# Edit environments/<environment-name>.yaml
# Change: domain: "OLD_ADDRESS"
# To: domain: "NEW_GATEWAY_ADDR"
  1. Re-sync ingress and services that depend on the domain:
# Re-sync gateway routes (picks up new domain)
HELMFILE_ENV=<environment-name> helmfile --selector release-group=ingress sync
# Re-sync services that embed the domain (API, admin-issuer-proxy)
HELMFILE_ENV=<environment-name> helmfile --selector release-group=services sync
HELMFILE_ENV=<environment-name> helmfile --selector name=admin-issuer-proxy sync
  1. Verify routes are using the new address:
kubectl get httproutes -A
kubectl get tcproutes -A

If you encounter issues during deployment, consult the self-hosted-troubleshooting guide for common problems and solutions.

Step 6: Verify the Installation

Verify the installation is successful by checking the pods are running and the helm releases are successful.

# View all pods with node assignment and status, should all be Running or Completed state
kubectl get pods -A -o wide
# Check helm releases status
helm list -A

Verify API Connectivity (Optional)

If you configured Gateway API ingress, you can verify the NVCF API is accessible by running the following commands.

  1. Set up environment variables:
# Get the Gateway address from Gateway quickstart
export GATEWAY_ADDR=$(kubectl get gateway nvcf-gateway -n envoy-gateway -o jsonpath='{.status.addresses[0].value}')
echo "Gateway Address: $GATEWAY_ADDR"
  1. Generate an admin token:
# Generate an admin API token
export NVCF_TOKEN=$(curl -s -X POST "http://${GATEWAY_ADDR}/v1/admin/keys" \
-H "Host: api-keys.${GATEWAY_ADDR}" \
| grep -o '"value":"[^"]*"' | cut -d'"' -f4)
echo "Token generated: ${NVCF_TOKEN:0:20}..."
  1. List functions. The list should be empty initially:
# List all functions
curl -s -X GET "http://${GATEWAY_ADDR}/v2/nvcf/functions" \
-H "Host: api.${GATEWAY_ADDR}" \
-H "Authorization: Bearer ${NVCF_TOKEN}" | jq .

Step 7. Configure the compute-plane Helmfile environment

Use deploy/stacks/nvcf-compute-plane from the selected bundle for each GPU cluster. The compute-plane Helmfile installs helm-nvca-operator and wires it to the control plane values returned by cluster registration.

Create an environment file in the compute-plane directory and merge the chart and image access settings into its global mapping. Set the service URLs to addresses reachable from the GPU cluster. If the GPU cluster reaches the control plane through one load balancer with hostname-based Gateway routing, keep the URL pointed at the load balancer and set the host-header overrides to the route hostnames.

cd "$BUNDLE_DIR"
touch deploy/stacks/nvcf-compute-plane/environments/<environment-name>.yaml
deploy/stacks/nvcf-compute-plane/environments/<environment-name>.yaml
global:
nvcaOperator:
selfManaged:
icmsServiceURL: "http://<GATEWAY_ADDR>"
icmsServiceHostHeaderOverride: "sis.<STACK_DOMAIN>"
revalServiceURL: "http://<GATEWAY_ADDR>"
revalServiceHostHeaderOverride: "reval.<STACK_DOMAIN>"
natsURL: "nats://<GATEWAY_ADDR>:4222"
natsHostOverride: "nats.<STACK_DOMAIN>"

If your GPU cluster can resolve per-service DNS names directly, set the service URLs to those names and omit the host-header override fields.

Create the pull secret in nvca-operator on the GPU cluster before installing NVCA. Helmfile references the secret from global.imagePullSecrets, and the operator propagates it to the managed namespaces after installation.

kubectl --kubeconfig <gpu-cluster-kubeconfig> \
create namespace nvca-operator --dry-run=client -o yaml | \
kubectl --kubeconfig <gpu-cluster-kubeconfig> apply -f -
kubectl --kubeconfig <gpu-cluster-kubeconfig> \
create secret docker-registry nvcr-pull-secret \
--docker-server=nvcr.io \
--docker-username='$oauthtoken' \
--docker-password="<registry-password>" \
--namespace=nvca-operator \
--dry-run=client -o yaml | kubectl --kubeconfig <gpu-cluster-kubeconfig> apply -f -

Enabling KAI Scheduler, Grove, and Dynamo

The KAI Scheduler, Grove, and Dynamo compute-plane add-ons are disabled by default. Enable only the add-ons that your Helm functions need, in the same environment file. Grove requires KAI Scheduler, and Dynamo requires Grove:

  • KAI PodGroup gang scheduling: kaiScheduler
  • Grove resources such as PodCliqueSet: kaiScheduler and groveOperator
  • Dynamo resources such as DynamoGraphDeployment: kaiScheduler, groveOperator, and dynamoOperator

For example, a cluster that runs Dynamo workloads enables all three:

deploy/stacks/nvcf-compute-plane/environments/<environment-name>.yaml
addons:
kaiScheduler:
enabled: true
groveOperator:
enabled: true
dynamoOperator:
enabled: true

Each add-on installs its component and allows its resource types in Helm functions. Without the matching add-on, NVCF rejects those functions with an error such as unsupported types: ["nvidia.com/v1alpha1.DynamoGraphDeployment"]. When KAI Scheduler is enabled, NVCA assigns every NVCF workload Pod on the cluster to KAI, not only gang-scheduled workloads.

To enable the add-ons on an installed GPU cluster, update the environment file and run the apply target with the values from Step 8:

make -C deploy/stacks/nvcf-compute-plane apply \
CLUSTER_NAME=<gpu-cluster-name> \
HELMFILE_ENV=<environment-name> \
NCA_ID=<nca-id> \
KUBECONFIG_FILE=<absolute-path-to-gpu-cluster-kubeconfig>

See Gang Scheduling for workload examples, KAI Scheduler for queue configuration, and Topology-Aware Scheduling for NVLink clique placement.

Step 8. Register and install each GPU cluster

Register each GPU cluster before installing NVCA. Registration discovers the GPU cluster’s OIDC issuer and JWKS, records them with SIS/ICMS, and writes the cluster identity values that the operator chart consumes.

Use KUBECONFIG_FILE for multi-cluster installs. It makes both registration and Helmfile target the GPU cluster instead of the control-plane cluster. For a complete Amazon EKS example, see the CSP End-to-End Example.

Export the installed control-plane environment before registration. For a split-cluster deployment, pass both contexts so the profile contains compute-reachable endpoints and reads trust from the control-plane cluster. Omit both context flags for a single-cluster deployment.

nvcf-cli-gpu-register.yaml
base_http_url: "http://<GATEWAY_ADDR>"
invoke_url: "http://<GATEWAY_ADDR>"
api_keys_service_url: "http://<GATEWAY_ADDR>"
icms_url: "http://<GATEWAY_ADDR>"
api_keys_host: "api-keys.<STACK_DOMAIN>"
api_host: "api.<STACK_DOMAIN>"
icms_host: "sis.<STACK_DOMAIN>"
invoke_host: "invocation.<STACK_DOMAIN>"
api_keys_service_id: "nvidia-cloud-functions-ncp-service-id-aketm"
api_keys_issuer_service: "nvcf-api"
api_keys_owner_id: "svc@nvcf-api.local"
client_id: "<nca-id>"
nvcf-cli --config <path-to-nvcf-cli-gpu-register.yaml> self-hosted \
--control-plane-stack deploy/stacks/self-managed \
--env <environment-name> \
--control-plane-context <control-plane-context> \
--compute-plane-context <gpu-cluster-context> \
control-plane profile export \
--cluster-name <control-plane-cluster-name> \
--region <region>
nvcf-cli --config <path-to-nvcf-cli-gpu-register.yaml> init

Run the compute-plane target from the extracted bundle root. The target writes deploy/stacks/nvcf-compute-plane/registration/<gpu-cluster-name>-register-values.yaml.

make -C deploy/stacks/nvcf-compute-plane register-cluster \
CLUSTER_NAME=<gpu-cluster-name> \
CLUSTER_REGION=<region> \
CONTROL_PLANE_PROFILE="$(pwd)/deploy/stacks/self-managed/out/control-plane-profile.yaml" \
KUBECONFIG_FILE=<absolute-path-to-gpu-cluster-kubeconfig> \
COMPUTE_KUBE_CONTEXT=<gpu-cluster-context> \
NVCF_CLI=<absolute-path-to-nvcf-cli> \
NVCF_CLI_CONFIG=<absolute-path-to-nvcf-cli-gpu-register.yaml>

Install the NVCA operator on that GPU cluster. The install target copies the registration file into out/ and runs Helmfile with HELMFILE_ENV=<environment-name>.

make -C deploy/stacks/nvcf-compute-plane install \
CLUSTER_NAME=<gpu-cluster-name> \
HELMFILE_ENV=<environment-name> \
NCA_ID=<nca-id> \
KUBECONFIG_FILE=<absolute-path-to-gpu-cluster-kubeconfig>

Verify the operator and backend on the GPU cluster:

kubectl --kubeconfig <gpu-cluster-kubeconfig> \
rollout status deployment/nvca-operator -n nvca-operator --timeout=10m
kubectl --kubeconfig <gpu-cluster-kubeconfig> \
get nvcfbackends -n nvca-operator
kubectl --kubeconfig <gpu-cluster-kubeconfig> \
get secret nvcr-pull-secret -n nvca-system
kubectl --kubeconfig <gpu-cluster-kubeconfig> \
get pods -n nvca-system

For a multi-cluster EKS install, this is the expected GPU cluster validation: the control plane is installed, each GPU cluster is registered with its own kubeconfig, the NVCA Operator is installed on that GPU cluster, and the NVCFBackend reports a healthy agent. Repeat registration, install, and health checks for each GPU cluster. Function execution also requires worker pods on the GPU cluster to reach the control-plane worker endpoints. Do not use function deployment or invocation as the acceptance check until those endpoints are reachable from the GPU cluster.

Repeat Step 8 for each additional GPU cluster. Use a unique CLUSTER_NAME for each cluster.

Next Steps

After the control plane and GPU clusters are installed, proceed to Self-Managed Clusters for NVCA operations and troubleshooting.

Uninstalling

This will delete all NVCF resources including data stored in persistent volumes. Ensure you have backups of any important data.

To remove the NVCF installation:

HELMFILE_ENV=<environment-name> helmfile destroy

After helmfile destroy completes, clean up the namespaces:

# Delete NVCF namespaces
kubectl delete namespace cassandra-system nats-system vault-system \
nvcf api-keys ess sis nvcf-ui \
--ignore-not-found

To also remove the Gateway infrastructure created by Gateway quickstart:

# Delete the Gateway and GatewayClass resources
kubectl delete gateway nvcf-gateway -n envoy-gateway --ignore-not-found
kubectl delete gatewayclass eg --ignore-not-found
# Uninstall Envoy Gateway
helm uninstall eg -n envoy-gateway-system
# Delete the gateway namespaces
kubectl delete namespace envoy-gateway envoy-gateway-system --ignore-not-found
# (Optional) Remove Gateway API CRDs if no longer needed
kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml