Deploy on Kubernetes with Helm¶
ONTAP-MCP includes an official Helm chart in this repository at charts/ontap-mcp.
Prerequisites¶
- Kubernetes 1.27+
- Helm 3.12+
- Network connectivity from the ONTAP-MCP pod to your ONTAP clusters
- If using
externalSecret.enabled(see Configuration): the External Secrets Operator CRDs and aSecretStore/ClusterSecretStorealready installed in the cluster. The chart only renders theExternalSecret, not the operator or the store.
Installing¶
helm install my-release oci://ghcr.io/netapp/charts/ontap-mcp --version <chart-version>
See the package page
for available versions. Nightly builds are published as 1.0.0-nightly
(overwritten on each nightly run); releases are published under the app's own
version, e.g. 26.4.0.
Or, from a local clone of this repository:
helm install my-release ./charts/ontap-mcp
This works out of the box with no ONTAP clusters configured yet. The server
starts but has nothing to poll, and the post-install notes say so. Provide
ontapConfig.data or ontapConfig.existingSecret via a values file to
configure real clusters.
Configuration¶
The server needs a config file, ontap.yaml, describing which ONTAP clusters
to connect to. The chart always sources that file from a Kubernetes Secret
mounted at /opt/mcp/ontap.yaml.
You can provide it in one of three ways:
- Let the chart create the
Secretby settingontapConfig.datato the full contents ofontap.yaml, in its native schema:
ontapConfig:
data:
Pollers:
cluster1:
addr: 10.0.0.1
username: admin
password: changeme
use_insecure_tls: false
See ontap-example.yaml
for the complete schema (Pollers, Defaults, McpAuth, Tls).
- Bring your own
Secret. This is recommended for anything beyond local testing. Source it from an external secrets manager through the CSI secrets-store driver or the External Secrets Operator instead of putting credentials in a values file.
Create a Secret with a key named ontap.yaml, then set
ontapConfig.existingSecret to its name. When this is set,
ontapConfig.data is ignored.
- Let the chart create and manage an
ExternalSecretfor you, by settingexternalSecret.enabledtotrue:
externalSecret:
enabled: true
secretStoreRef:
name: my-secret-store
kind: ClusterSecretStore
data:
- secretKey: ontap.yaml
remoteRef:
key: secret/ontap-mcp-config
secretStoreRef, refreshInterval, data/dataFrom, and template map
directly onto the External Secrets Operator's own ExternalSecret spec --
see its templating guide
for composing ontap.yaml from more than one remote key. When this is
set, the chart does not render its own Secret and ontapConfig.data is
ignored, same as with ontapConfig.existingSecret.
If the credentials live as separate keys in the secret backend rather
than one ready-made ontap.yaml blob, use template to compose them:
externalSecret:
enabled: true
secretStoreRef:
name: my-secret-store
kind: ClusterSecretStore
data:
- secretKey: cluster1_addr
remoteRef:
key: ontap-cluster1-addr
- secretKey: cluster1_user
remoteRef:
key: ontap-cluster1-user
- secretKey: cluster1_password
remoteRef:
key: ontap-cluster1-password
template:
engineVersion: v2
data:
ontap.yaml: |
Pollers:
cluster1:
addr: {{ .cluster1_addr }}
username: {{ .cluster1_user }}
password: {{ .cluster1_password }}
use_insecure_tls: false
Most current ESO installs are on the external-secrets.io/v1 API, which
is the default. Set externalSecret.apiVersion to
external-secrets.io/v1beta1 if your cluster's ESO hasn't been upgraded
yet.
The chart mounts ontap.yaml via subPath, so kubelet does not
propagate an in-place Secret update into the running container. When ESO
refreshes the Secret on refreshInterval, roll the deployment yourself
to pick up the change, e.g. kubectl rollout restart deployment. This
also applies to ontapConfig.existingSecret; only ontapConfig.data
triggers an automatic rollout, via a checksum pod annotation.
If none of these are set, the server still starts with no configured clusters.
Exposing the service¶
By default the chart only creates a ClusterIP Service. Three exposure modes
are available, all disabled by default:
ingress.enabled: truecreates a plain KubernetesIngress.httpRoute.enabled: truecreates a Gateway APIHTTPRoute. This requires the Gateway API to be installed and aGatewayto attach to viahttpRoute.parentRefs.listenerSet.enabled: truecreates a Gateway APIListenerSet. This requires Gateway API 1.5 or newer and is useful whenhttpRouteshould attach to a shared per-app listener instead of directly to aGateway.
Multiple replicas¶
The server keeps per-session MCP state in-process. If you scale
replicaCount above 1, also add --stateless to extraArgs and make sure
your Service or Ingress does not rely on sticky sessions.
Upgrade¶
helm upgrade --install my-release ./charts/ontap-mcp
Or, for a release installed from the OCI chart:
helm upgrade --install my-release oci://ghcr.io/netapp/charts/ontap-mcp --version <chart-version>
Uninstall¶
helm uninstall my-release
k3d smoke test¶
Use this to verify the chart behavior in a local k3d cluster:
k3d cluster create ontap-mcp-smoke
helm lint ./charts/ontap-mcp
helm install ontap-mcp ./charts/ontap-mcp
kubectl rollout status deployment/ontap-mcp --timeout=120s
kubectl port-forward service/ontap-mcp 8080:8080
curl -i http://127.0.0.1:8080/health
Expected response:
- HTTP status
200 OK - Body
OK
Delete the local test cluster when done:
k3d cluster delete ontap-mcp-smoke